Mercurial > repos > other > Puppet
changeset 332:7198cc4397d8
Remove custom LogWatch scripts
There's no point in maintaining them manually when the OS should
(in theory) keep the scripts up-to-date with the services
We were getting more errors on CentOS 8 for having out of date
custom scripts than for having OS-supplied ones
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sun, 15 Mar 2020 16:51:11 +0000 |
parents | f69e2d197302 |
children | 8d8dd5c4ec0e |
files | common/logwatch/applyhttperrordate common/logwatch/dovecot common/logwatch/http-error common/logwatch/log-http-error.conf common/logwatch/logfiles_http-error.conf common/logwatch/named common/logwatch/postfix common/logwatch/scripts_mysql common/logwatch/scripts_php common/logwatch/services-fail2ban common/logwatch/services-http-error.conf common/logwatch/services_mysql.conf manifests/templates.pp |
diffstat | 13 files changed, 34 insertions(+), 7403 deletions(-) [+] |
line wrap: on
line diff
--- a/common/logwatch/applyhttperrordate Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -########################################################################## -# $Id: applystddate,v 1.19 2008/03/24 23:31:27 kirk Exp $ -########################################################################## - -######################################################## -## Copyright (c) 2008 Kirk Bauer -## 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. -######################################################### - -use Logwatch ':dates'; - -my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0; - -$SearchDate = TimeFilter('%a %b %d %H:%M:%S %Y'); - -# The date might be "Dec 09", but it needs to be "Dec 9"... -#$SearchDate =~ s/ 0/ /; - -if ( $Debug > 5 ) { - print STDERR "DEBUG: Inside ApplyStdDate...\n"; - print STDERR "DEBUG: Looking For: " . $SearchDate . "\n"; -} - -while (defined($ThisLine = <STDIN>)) { - if ($ThisLine =~ m/^\[$SearchDate\]/o) { - print $ThisLine; - } -} - -# vi: shiftwidth=3 syntax=perl tabstop=3 et -# Local Variables: -# mode: perl -# perl-indent-level: 3 -# indent-tabs-mode: nil -# End:
--- a/common/logwatch/dovecot Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,590 +0,0 @@ -######################################################## -# $Id: dovecot,v 1.18 2010/09/18 17:41:00 stefan Exp $ -######################################################## -# $Log: dovecot,v $ -# Revision 1.18 2010/09/18 17:41:00 stefan -# ignore: ssl-build-param -# -# Revision 1.17 2009/06/02 14:48:06 mike -# Removed some periods that were in the Fedora patch and broke the file -mgt -# -# Revision 1.16 2009/06/02 14:45:48 mike -# Patch from Fedora (Ivana Hutarova Varekova) -mgt -# -# Revision 1.15 2008/11/18 06:02:49 mike -# Rolled back..that was wrong. -mgt -# -# Revision 1.14 2008/11/18 06:00:34 mike -# Removed a space should be better -mgt -# -# Revision 1.13 2008/11/18 04:32:49 mike -# Added bytes detected to IMAP disconnect match expect more issues -mgt -# -# Revision 1.12 2008/08/11 15:38:02 mike -# Connection closed patch from Niels Baggesen -mgt -# -# Revision 1.11 2008/06/30 23:07:51 kirk -# fixed copyright holders for files where I know who they should be -# -# Revision 1.10 2008/03/24 23:31:26 kirk -# added copyright/license notice to each script -# -# Revision 1.9 2008/02/14 18:19:51 mike -# Patch from Gilles Detillieux summarize pop3/imap -mgt -# -# Revision 1.8 2008/01/16 20:11:04 bjorn -# Filtering dovecot start-up message, by Gilles Detillieux. -# -# Revision 1.7 2007/06/18 03:54:45 bjorn -# Better printing of IPv6 addresses, by Patrick Vande Walle. -# -# Revision 1.6 2007/03/17 19:13:13 bjorn -# Now handling dovecot starts/kills. -# -# Revision 1.5 2006/12/20 15:25:09 bjorn -# Additional filtering, by Ivana Varekova. -# -# Revision 1.4 2006/08/13 22:02:31 bjorn -# IPv4 addresses displayed in native format, and don't display user totals -# if user connects from only one IP address; changes by Patrick Vande Walle. -# -# Revision 1.3 2006/08/13 21:06:33 bjorn -# Added support for Dovecot 1.0 based on patches from Mark Nienberg, and -# IP addresses displayed without brackets for consistency across versions; -# modifications by Patrick Vande Walle. -# -# Revision 1.2 2005/12/07 04:31:44 bjorn -# Added $dovecot_ignore_host. -# -# Revision 1.1 2005/09/18 17:01:05 bjorn -# Dovecot filters written by Patrick Vande Walle. -# -######################################################## -# Please send all comments, suggestions, bug reports, -# etc, to logwatch-devel@lists.sourceforge.net -######################################################## -# The Dovecot script was written by: -# Patrick Vande Walle <patrick@isoc.lu> -# Based on previous work by -# Pawel Golaszewski <blues@gda.pl> -# -# TODO: -# - use printf features to align text in table -# -######################################################## - -######################################################## -## Copyright (c) 2008 Patrick Vande Walle -## 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 $Debug = $ENV{'LOGWATCH_DEBUG'} || 0; -my $Detail = $ENV{'dovecot_detail'} || $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; -my $IgnoreHost = $ENV{'dovecot_ignore_host'} || ""; - -my $Restarts = 0; -my $End = 0; - -if ( $Debug >= 5 ) { - print STDERR "\n\nDEBUG \n\n"; -} - -use Socket; -my $rdns = {}; -sub hostName { - (my $ipaddr) = @_; - - if ($ENV{'LOGWATCH_NUMERIC'} || $ENV{'dovecot_numeric'}) { - return $ipaddr; - } - - if (exists $rdns{ $ipaddr }) { - return $rdns{ $ipaddr }; - } - $rdns{ $ipaddr } = $ipaddr; - - my $iaddr = inet_aton($ipaddr); - if (defined $iaddr) { - my $host = gethostbyaddr($iaddr, AF_INET); - if (defined $host) { - my $iaddrcheck = gethostbyname($host); - if (defined $iaddrcheck) { - if ($iaddr == $iaddrcheck) { - $rdns{ $ipaddr } = $host; - } - } - } - } - return $rdns{ $ipaddr }; -} - -# Handle "dovecot: <svc>" and "dovecot: [ID yyyyy mail.info] <svc" -my $dovecottag = qr/dovecot:(?:\s*\[[^]]+\])?/; - -while (defined($ThisLine = <STDIN>)) { - # remove timestamp. We can't use *RemoveHeaders because we need the - # service name - $ThisLine =~ s/^\w{3} .\d \d\d:\d\d:\d\d (?:[^\s:]* |)//; - if ( ($ThisLine =~ /(?:ssl-build-param|ssl-params): SSL parameters regeneration completed/) or - ($ThisLine =~ /ssl-params: Generating SSL parameters/) or - ($ThisLine =~ /auth-worker/) or - ($ThisLine =~ /auth:.*: Connected to/) or - ($ThisLine =~ /Connection closed/) or - ($ThisLine =~ /IMAP.*: Connection closed bytes/) or - ($ThisLine =~ /IMAP.* failed with mbox file/) or - ($ThisLine =~ /discarded duplicate forward to/) or - ($ThisLine =~ /discarding vacation response/) - ) - { - # We don't care about these - } elsif ( $ThisLine =~ /Killed with signal /) { - $End++; - } elsif ( $ThisLine =~ /Dovecot (v\d[^ ]* |)starting up( \(.*\))?$/) { - $Restarts++; - $End = 0; - } elsif ( ( ($User, $Host) = ( $ThisLine =~ /^pop3-login: Login: (.*?) \[(.*)\]/ ) ) or - ( ($User, $Host) = ( $ThisLine =~ /^pop3-login: Info: Login: user=\<(.*?)\>.*rip=(.*)\, lip=/ ) ) ) { - if ($Host !~ /$IgnoreHost/) { - $Host = hostName($Host); - $Login{$User}{$Host}++; - $LoginPOP3{$User}++; - $ConnectionPOP3{$Host}++; - $Connection{$Host}++; - } - } elsif ( ( ($User, $Host) = ( $ThisLine =~ /^imap-login: Login: (.*?) \[(.*)\]/ ) ) or - ( ($User, $Host) = ( $ThisLine =~ /^imap-login: Info: Login: user=\<(.*?)\>.*rip=(.*)\, lip=/ ) ) ) { - if ($Host !~ /$IgnoreHost/) { - $Host = hostName($Host); - $Login{$User}{$Host}++; - $LoginIMAP{$User}++; - $ConnectionIMAP{$Host}++; - $Connection{$Host}++; - } - } elsif ( ($User, $Host) = ( $ThisLine =~ /managesieve-login: Login: user=\<(.*?)\>.*rip=(.*)\, lip=/ ) ) { - if ($Host !~ /$IgnoreHost/) { - $Host = hostName($Host); - $SieveLogin{$User}{$Host}++; - $LoginSieve{$User}++; - $ConnectionSieve{$Host}++; - $Connection{$Host}++; - } - -# 'lda' for dovecot 2.0, 'deliver' for earlier versions - } elsif ( ($User, $Mailbox) = ( $ThisLine =~ /^$dovecottag (?:lda|deliver)\((.*)\): msgid=.*: saved mail to (.*)/ ) ) { - $Deliver{$User}{$Mailbox}++; - -# For Sieve-based delivery - } elsif ( ($User, $Mailbox) = ( $ThisLine =~ /^$dovecottag (?:lda|deliver)\((.*)\): sieve: msgid=.*: stored mail into mailbox '(.*)'/ ) ) { - $Deliver{$User}{$Mailbox}++; - -# LMTP-based delivery - } elsif ( ($User, $Mailbox) = ( $ThisLine =~ /^$dovecottag lmtp\(\d+, (.*)\): [^:]+: msgid=.*: saved mail to (.*)/ ) ) { - # dovecot: [ID 583609 mail.info] lmtp(12782, cloyce@headgear.org): jBt1EfjCMk3uMQAAm9eMBA: msgid=<4D32DB1F.3080707@c-dot.co.uk>: saved mail to INBOX - $Deliver{$User}{$Mailbox}++; - -# sieve forward - } elsif (($User, $Recip) = ($ThisLine =~ /^$dovecottag (?:lda|deliver)\((.*)\): sieve: msgid=.* forwarded to \<(.*)\>/)) { - $Forwarded{$User}{$Recip}++; - -# sieve vacation - } elsif (($User, $Recip) = ($ThisLine =~ /^$dovecottag (?:lda|deliver)\((.*)\): sieve: msgid=.* sent vacation response to \<(.*)\>/)) { - $VacationResponse{$User}{$Recip}++; - - } elsif (($User, $Recip) = ($ThisLine =~ /^$dovecottag (?:lda|deliver)\((.*)\): sieve: msgid=.* discarded duplicate vacation response to \<(.*)\>/ )) { - $VacationDup{$User}{$Recip}++; - - } elsif ( $ThisLine =~ /^$dovecottag (?:lda|deliver)\(.*\): sieve: msgid=.* marked message to be discarded if not explicitly delivered/ ) { - # dovecot: lda(joe): sieve: msgid=<m$01$@com>: marked message to be discarded if not explicitly delivered (discard action) - # IGNORE - } elsif ( $ThisLine =~ /^$dovecottag lmtp\(.*\): Connect from/ ) { - # dovecot: [ID 583609 mail.info] lmtp(12782): Connect from local: 1 Time(s) - # IGNORE - - } elsif ( $ThisLine =~ /^$dovecottag lmtp\(.*\): Disconnect from/ ) { - # dovecot: [ID 583609 mail.info] lmtp(12782): Disconnect from local: Client quit: 1 Time(s) - # IGNORE - - -# This is for Dovecot 1.0 series - } elsif ( ($User, $Host) = ( $ThisLine =~ /^$dovecottag pop3-login: Login: user=\<(.*?)\>.*rip=(.*)\, lip=/ ) ) { - if ($Host !~ /$IgnoreHost/) { - $Host = hostName($Host); - $Login{$User}{$Host}++; - $LoginPOP3{$User}++; - $ConnectionPOP3{$Host}++; - $Connection{$Host}++; - } - } elsif ( ($User, $Host) = ( $ThisLine =~ /^$dovecottag imap-login: Login: user=\<(.*?)\>.*rip=(.*)\, lip=/) ) { - if ($Host !~ /$IgnoreHost/) { - $Host = hostName($Host); - $Login{$User}{$Host}++; - $LoginIMAP{$User}++; - $ConnectionIMAP{$Host}++; - $Connection{$Host}++; - } - - # Dovecot 2.0 proxy - } elsif ( ($User, $Host) = ( $ThisLine =~ /^$dovecottag pop3-login: proxy\((.*)\): started proxying to .*: user=<.*>, method=.*, rip=(.*), lip=/ ) ) { - if ($Host !~ /$IgnoreHost/) { - $ProxyLogin{$User}{$Host}++; - $ProxyLoginPOP3{$User}++; - $ProxyConnectionPOP3{$Host}++; - $ProxyConnection{$Host}++; - } - } elsif ( ($User, $Host) = ( $ThisLine =~ /^$dovecottag imap-login: proxy\((.*)\): started proxying to .*: user=<.*>, method=.*, rip=(.*), lip=/ ) ) { - if ($Host !~ /$IgnoreHost/) { - $ProxyLogin{$User}{$Host}++; - $ProxyLoginIMAP{$User}++; - $ProxyConnectionIMAP{$Host}++; - $ProxyConnection{$Host}++; - } - } elsif ( ($Reason) = ( $ThisLine =~ /proxy\(.*\): disconnecting .* \(Disconnected (.*)\)/ ) ) { - $ProxyDisconnected{$Reason}++; - - } elsif ($ThisLine =~ /Disconnected (\[|top)/) { - $Disconnected{"no reason"}++; - } elsif (($Reason) = ($ThisLine =~ /Disconnected: (.*) \[/) ) { - $Disconnected{$Reason}++; - } elsif (($Reason) = ($ThisLine =~ /Disconnected: (.*) (bytes|top|in)=.*/) ) { - $Disconnected{$Reason}++; - } elsif ($ThisLine =~ /Logged out (rcvd|bytes|top|in)=.*/) { - $Disconnected{"Logged out"}++; - } elsif (($Reason) = ($ThisLine =~ /Disconnected \((.*)\):/) ) { - $Disconnected{$Reason}++; - } elsif (($Reason, $Host) = ($ThisLine =~ /TLS initialization failed/) ) { - $TLSInitFail++; - } elsif (($Host) = ($ThisLine =~ /Aborted login \[(.*)\]/) ) { - $Host = hostName($Host); - $Aborted{$Host}++; - } elsif (($Reason) = ($ThisLine =~ /Aborted login \((.*)\):/)) { - $Aborted{$Reason}++; - } elsif (($user, $rip, $lip) = ($ThisLine =~ /Maximum number of connections.* exceeded.* user=<([^>]+)>.*rip=([^,]+), lip=([^,]+)/)) { - # dovecot: [ID 583609 mail.info] imap-login: Maximum number of connections from user+IP exceeded (mail_max_userip_connections=10): user=<cloyce@headgear.org>, method=CRAM-MD5, rip=102.225.17.52, lip=14.105.322.67, TLS - $LimitExceeded{"max_userip_connections: $user from $rip to $lip"}++; - -# This is for Dovecot 1.0 series -# Overly general matches in this section -mgt - - } elsif ($ThisLine =~ /Disconnected for inactivity/) { - $Disconnected{"Inactivity"}++; - } elsif ($ThisLine =~ /Disconnected in IDLE/) { - $Disconnected{"in IDLE"}++; - } elsif ($ThisLine =~ /Disconnected in APPEND/) { - $Disconnected{"in APPEND"}++; - } elsif (($ThisLine =~ /Disconnected$/) or - ($ThisLine =~ /(IMAP|POP3)\(.+\): Disconnected (bytes|top|rip|user|method)=/) or - ($ThisLine =~ /(imap\-login|pop3\-login): Disconnected: (bytes|top|rip|user|method)=/) ) { - $Disconnected{"no reason"}++; - } elsif ( (($Reason) = ($ThisLine =~ /(?:IMAP|POP3).+: Disconnected: (.+) (bytes|top|in)=/i)) or - (($Reason) = ($ThisLine =~ /(?:imap\-login|pop3\-login): Disconnected: \(?(.+)\)?: /)) or - #This one should go away also -mgt - (($Reason) = ($ThisLine =~ /IMAP.+: Disconnected: (.+)/i)) ) { - $Disconnected{$Reason}++; - } elsif ($ThisLine =~ /(IMAP|POP3).+: Connection closed (top|bytes)=/i) { - $ConnectionCl{"no reason"}++; - } elsif ( ($Reason) = ($ThisLine =~ /(?:IMAP|POP3).+: Connection closed: (.*) (?:bytes|method|top|rip|user)=/i) ) { - $ConnectionCl{$Reason}++; - } elsif ($ThisLine =~ /(IMAP|POP3).+: (Connection closed.*)/) { - $Disconnected{$2}++; - } elsif (($Host) = ($ThisLine =~ /(?:imap\-login|pop3\-login): Aborted login: .*rip=(?:::ffff:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/) ) { - $Aborted{$Host}++; - } elsif (($Error) = ($ThisLine =~ /child \d* (?:\(login\) )?returned error (.*)/)) { - # dovecot: child 23747 (login) returned error 89 - # dovecot: log: Error: service(auth): child 19654 returned error 89 (Fatal failure) - $ChildErr{$Error}++; - } elsif (($Name) = ($ThisLine =~ /$dovecottag IMAP\((.*)\): .*(.*) failed: Disk quota exceeded/i)) { - # dovecot: IMAP(podracka): mkdir(/home/LF/KLINIKY/podracka/mail/.imap/saved-messages) failed: Disk quota exceeded - $DiskQuotaExceed{$Name}++; - } else { - # Report any unmatched entries... - chomp($ThisLine); - $OtherList{$ThisLine}++; - } -} - -################################################ - -if ( $End ) { - print "\nDovecot was killed, and not restarted afterwards.\n"; -} - -if ( ( $Detail >=5 ) and $Restarts ) { - print "\nDovecot restarted $Restarts time(s).\n"; -} - -if ( ( $Detail >= 5 ) and (keys %Connection)) { - print "\n[Dovecot IMAP and POP3] Connections:". - "\n====================================". - "\nPOP3 IMAP Total Host". - "\n" . "-" x 72; - - $TLSInitFail = 0; - foreach $Host (sort { $Connection{$b} <=> $Connection{$a} } - keys %Connection) { - $Total = $Connection{$Host}; - if (defined ($ConnectionPOP3{$Host})) { - $Conns = $ConnectionPOP3{$Host}; - } else { - $Conns = 0; - } - if (defined ($ConnectionIMAP{$Host})) { - $IMAP = $ConnectionIMAP{$Host}; - } else { - $IMAP = 0; - } -# Cleanly display IPv4 addresses - $Host=~ s/::ffff://; - printf "\n%4s %4s %5s %s", $Conns, $IMAP, $Total, $Host; - $POP3Count += $Conns; - $IMAPCount += $IMAP; - $TotalCount += $Total; - } - print "\n" . "-" x 72; - printf "\n%4s %4s %5s %s", $POP3Count, $IMAPCount, $TotalCount, "Total"; -} - -if (keys %Deliver) { - my $DeliverCount = 0; - my $DeliverUserCount = {}; - foreach my $User (keys %Deliver) { - foreach my $Mailbox (keys %{$Deliver{$User}}) { - $DeliverUserCount{$User} += $Deliver{$User}{$Mailbox}; - } - $DeliverCount += $DeliverUserCount{$User}; - } - printf "\n" if ($Detail >= 5); - printf "\nDovecot Deliveries: %s", $DeliverCount; - if ($Detail >= 5) { - foreach my $User (sort { $DeliverUserCount{$b} <=> $DeliverUserCount{$a} } - keys %DeliverUserCount) { - printf "\n %4s %s", $DeliverUserCount{$User}, $User; - if ($Detail >= 10) { - foreach my $Mailbox (sort { - $Deliver{$User}{$b} <=> $Deliver{$User}{$a} - } keys %{$Deliver{$User}}) { - printf "\n %4s %s", $Deliver{$User}{$Mailbox}, $Mailbox; - } - } - } - } -} - -if (($Detail >= 10) and (keys %Forwarded)) { - $TotalForwarded = 0; - - print "\n\nDovecot LDA sieve forwards:"; - foreach $User (sort keys %Forwarded) { - print "\n\n User $User"; - foreach my $Recip (sort keys %{$Forwarded{$User}}) { - print "\n To $Recip: $Forwarded{$User}{$Recip} time(s)"; - $TotalForwarded += $Forwarded{$User}{$Recip}; - } - } - print "\n\n Total: $TotalForwarded Time(s)"; -} - -if (($Detail >= 10) and (keys %VacationResponse)) { - $TotalVacResp = 0; - print "\n\nDovecot LDA sieve vacation responses:"; - foreach my $User (sort keys %VacationResponse) { - print "\n\n User $User"; - foreach my $Recip (sort keys %{$VacationResponse{$User}}) { - print "\n To $Recip: $VacationResponse{$User}{$Recip} time(s)"; - $TotalVacResp += $VacationResponse{$User}{$Recip}; - } - } - print "\n\n Total: $TotalVacResp Time(s)"; -} - -if (($Detail >= 10) and (keys %VacationDup)) { - $TotalVacDup = 0; - print "\n\nDovecot LDA sieve duplicate vacation responses not sent:"; - foreach my $User (sort keys %VacationDup) { - print "\n User $User"; - foreach my $Recip (sort keys %{$VacationDup{$User}}) { - print "\n To $Recip: $VacationDup{$User}{$Recip} time(s)"; - $TotalVacDup += $VacationDup{$User}{$Recip}; - } - } - print "\n\n Total: $TotalVacDup Time(s)"; -} - - -if (keys %Login) { - my $LoginCount = 0; - my $LoginUserCount = {}; - foreach my $User (keys %Login) { - foreach my $Host (keys %{$Login{$User}}) { - $LoginUserCount{$User} += $Login{$User}{$Host}; - } - $LoginCount += $LoginUserCount{$User}; - $LoginPOP3{$User} = 0 if $LoginPOP3{$User} <= 0; - $LoginIMAP{$User} = 0 if $LoginIMAP{$User} <= 0; - } - printf "\n" if ($Detail >= 5); - printf "\nDovecot IMAP and POP3 Successful Logins: %s", $LoginCount; - if ($Detail >= 5) { - foreach my $User (sort { $LoginUserCount{$b} <=> $LoginUserCount{$a} } - keys %LoginUserCount) { - printf("\n %4s %s", $LoginUserCount{$User}, $User); - if ($Detail >= 10) { - printf(" (%s POP3, %s IMAP)", $LoginPOP3{$User}, $LoginIMAP{$User}); - foreach my $Host (sort { $Login{$User}{$b} <=> $Login{$User}{$a} } - keys %{$Login{$User}}) { - $HostCount = $Login{$User}{$Host}; - # Cleanly display IPv4 addresses - $Host=~ s/::ffff://; - printf "\n %4s %s", $Login{$User}{$Host}, $Host; - } - } - } - } -} - -if ( ( $Detail >= 10 ) and (keys %SieveLogin)) { - print "\n\nDovecot ManageSieve Successful Logins:"; - $LoginCount = 0; - foreach my $User (sort keys %SieveLogin) { - print "\n\n User $User:"; - $UserCount = 0; - $NumHosts = 0; - foreach $Host (sort keys %{$SieveLogin{$User}}) { - $NumHosts++; - $HostCount = $SieveLogin{$User}{$Host}; -# Cleanly display IPv4 addresses - $Host=~ s/::ffff://; - print "\n From $Host: $HostCount Time(s)"; - $UserCount += $HostCount; - } - $LoginCount += $UserCount; - if ($NumHosts > 1) { - print "\n Total: $UserCount Time(s)"; - } - } - print "\n\nTotal: $LoginCount successful ManageSieve logins"; -} - -if (keys %LimitExceeded) { - print "\n\nDovecot limits exceeded:"; - foreach my $Reason (sort keys %LimitExceeded) { - print "\n $Reason: $LimitExceeded{$Reason} Time(s)"; - } -} - -if (keys %Disconnected) { - my $Disconnects = 0; - foreach my $Reason (%Disconnected) { - $Disconnects += $Disconnected{$Reason}; - } - printf "\n" if ($Detail >= 5); - printf "\nDovecot disconnects: %s", $Disconnects; - if ($Detail >= 5) { - foreach my $Reason (sort { $Disconnected{$b} <=> $Disconnected{$a} } - keys %Disconnected) { - printf "\n %4s %s", $Disconnected{$Reason}, $Reason; - } - } -} - -if (keys %ConnectionCl) { - print "\n\nDovecot connections closed:"; - foreach my $Reason (sort keys %ConnectionCl) { - print "\n $Reason: $ConnectionCl{$Reason} Time(s)"; - } -} - -if (keys %ChildErr) { - print "\n\nDovecot child error:"; - foreach my $Error (sort keys %ChildErr) { - print "\n error number ". $Error . ": ". $ChildErr{$Error} ." Time(s)"; - } -} - -if ((keys %Aborted) && ($Detail >= 10)) { - print "\n\nLogout/aborts:"; - foreach my $Host (sort keys %Aborted) { - print "\n $Host: $Aborted{$Host} Time(s)"; - } -} - -if ($TLSInitFail > 0) { - print "\n\nTLS Initialization failed $TLSInitFail Time(s)"; -} - -if (keys %DiskQuotaExceed) { - print "\n\nDisk quota exceeded:"; - foreach my $Name (sort keys %DiskQuotaExceed) { - print "\n disk quota for user '". $Name . "' exceeded: ". $DiskQuotaExceed{$Name} ." Time(s)"; - } -} - -if ( ( $Detail >= 5 ) and (keys %ProxyLogin)) { - print "\n\nDovecot Proxy IMAP and POP3 Successful Logins:"; - $LoginCount = 0; - foreach my $User (sort keys %ProxyLogin) { - print "\n User $User:"; - if ( ($Detail >= 10) and ($ProxyLoginPOP3{$User} > 0 || $ProxyLoginIMAP{$User} > 0) ) { - print " ("; - if ($ProxyLoginPOP3{$User} > 0) { print "$ProxyLoginPOP3{$User} POP3"; }; - if ($ProxyLoginPOP3{$User} > 0 && $ProxyLoginIMAP{$User} > 0) { print "/"; }; - if ($ProxyLoginIMAP{$User} > 0) { print "$ProxyLoginIMAP{$User} IMAP"; }; - print ")"; - } - $UserCount = 0; - $NumHosts = 0; - foreach $Host (sort keys %{$ProxyLogin{$User}}) { - $NumHosts++; - $HostCount = $ProxyLogin{$User}{$Host}; -# Cleanly display IPv4 addresses - $Host=~ s/::ffff://; - print "\n From $Host: $HostCount Time(s)" if ($Detail >= 10); - $UserCount += $HostCount; - } - $LoginCount += $UserCount; - if ($Detail >= 10) { - if ($NumHosts > 1) { - print "\n Total: $UserCount Time(s)\n"; - } else { - print "\n"; - } - } elsif ($Detail >= 5) { - print " $UserCount Time(s)"; - } - } - print "\nTotal: $LoginCount successful logins"; -} - -if (keys %ProxyDisconnected) { - print "\n\nDovecot Proxy disconnects:\n"; - foreach my $Reason (sort keys %ProxyDisconnected) { - print " $Reason: $ProxyDisconnected{$Reason} Time(s)\n"; - } -} - -if (keys %OtherList) { - print "\n\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 syntax=perl et -# Local Variables: -# mode: perl -# perl-indent-level: 3 -# indent-tabs-mode: nil -# End:
--- a/common/logwatch/http-error Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -#!/usr/bin/perl -# -########################################################################## -## Copyright (c) 2016 Logwatch -## 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. -########################################################################## - -use diagnostics; -use strict; - -my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; - -sub CustomizeErrorString { - my ($LogLevel, $ErrorCode, $Description) = @_; - # This function is only invoked when detail is set to 8 or 9. - # Here you would modify the Description. Some Description strings - # may differ only on some printed parameters, and it is preferable - # to group them together. Examples of these may be process numbers, - # IP addresses, port numbers, or file names. The purpose of this - # function is to "collapse" these different messages into the same - # array entry. - - # For now, simply return the string. - return($Description); -} - -my %LogMessages = (); -my $MatchFilter = $ENV{'http_error_matchfilter'} || ""; -my $ReportFilter = $ENV{'http_error_reportfilter'} || ""; - -while (defined(my $ThisLine = <STDIN>)) { - if (my ($LogLevel, $ErrorCode, $Description) = - ($ThisLine =~ /:(.*?)\].*(AH\d{5}): (.*)/) ) { - # $MatchFilter is a variable that is set by setting the - # $HTTP_Error_MatchFilter variable in the conf/services/http-error.conf - # file. It is executed here, before any other matching statements. - eval $MatchFilter; - if ($@) { - print $@; - print "While processing MatchFilter:\n$MatchFilter\n"; - } - # $ThisLine might have been reset (undef, or empty string) in $MatchFilter - next unless $ThisLine; - - if (($Detail == 8) || ($Detail == 9)) { - $Description = CustomizeErrorString($LogLevel, $ErrorCode, $Description); - } - if (($Detail >= 1) || ($LogLevel =~ "emerg|alert|crit|error")) { - $LogMessages{$LogLevel}{$ErrorCode}{$Description}++; - } - } -} - -# $ReportFilter is a variable that is set by setting the -# $HTTP_Error_ReportFilter variable in the conf/services/http-error.conf -# file. It is executed here, before any other printing statements. -eval $ReportFilter; -if ($@) { - print $@; - print "While processing ReportFilter:\n$ReportFilter\n"; -} - -if (keys %LogMessages) { - my $Count = 0; - foreach my $LogLevel (keys %LogMessages) { - printf("\nLevel %-6s", $LogLevel); - foreach my $ErrorCode (keys %{$LogMessages{$LogLevel}}) { - print "\n Error Code: $ErrorCode" if $Detail >= 5; - foreach my $Description (keys %{$LogMessages{$LogLevel}{$ErrorCode}}) { - if ($Detail >= 9) { - print "\n $Description: "; - print "$LogMessages{$LogLevel}{$ErrorCode}{$Description} Time(s)"; - } - $Count += $LogMessages{$LogLevel}{$ErrorCode}{$Description}; - } # foreach $Description - if (($Detail >= 5) && ($Detail < 9)) { - printf(": %5d Time(s)", $Count); - $Count = 0; - if ($Detail >=6) { - print "\n E.g.: "; - # print only first entry (index 0) - my $EG_string = (keys %{$LogMessages{$LogLevel}{$ErrorCode}})[0]; - if (($Detail == 6) && (length($EG_string) > 66)) { - printf ("%.62s ...", $EG_string); - } else { - print $EG_string; - } - } - } - } # foreach $ErrorCode - if ($Detail < 5) { - printf("%s%5d%s", ": ", $Count, " Time(s)"); - $Count = 0; - } - } # foreach $LogLevel -} # if keys %LogMessages - -exit(0); - -# vi: shiftwidth=3 tabstop=3 syntax=perl et -# Local Variables: -# mode: perl -# perl-indent-level: 3 -# indent-tabs-mode: nil -# End: \ No newline at end of file
--- a/common/logwatch/log-http-error.conf Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -######################################################## -# Define log file group for httpd -######################################################## - -# What actual file? Defaults to LogPath if not absolute path.... -LogFile = apache/error_*.log -LogFile = apache/error_*.log.1 - - -# If the archives are searched, here is one or more line -# (optionally containing wildcards) that tell where they are... -#If you use a "-" in naming add that as well -mgt -Archive = apache/error_*.log.*.gz - -# Expand the repeats (actually just removes them now) -*ExpandRepeats - - -# Keep only the lines in the proper date range... -#*ApplyHttpErrorDate -*ApplyStdDate = "\[%a %b %d %H:%M:%S.\d{6} %Y\]" -*RemoveHeaders = "\[\w{3} \w{3} \d{1,2} \d\d:\d\d:\d\d(\.\d*)? \d{4}\] " - -# vi: shiftwidth=3 tabstop=3 et
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/logwatch/logfiles_http-error.conf Sun Mar 15 16:51:11 2020 +0000 @@ -0,0 +1,23 @@ +######################################################## +# Define log file group for httpd +######################################################## + +# What actual file? Defaults to LogPath if not absolute path.... +LogFile = apache/error_*.log +LogFile = apache/error_*.log.1 + + +# If the archives are searched, here is one or more line +# (optionally containing wildcards) that tell where they are... +#If you use a "-" in naming add that as well -mgt +Archive = apache/error_*.log.*.gz + +# Expand the repeats (actually just removes them now) +*ExpandRepeats + + +# Keep only the lines in the proper date range... +*ApplyStdDate = "\[%a %b %d %H:%M:%S.\d{6} %Y\]" +*RemoveHeaders = "\[\w{3} \w{3} \d{1,2} \d\d:\d\d:\d\d(\.\d*)? \d{4}\] " + +# vi: shiftwidth=3 tabstop=3 et
--- a/common/logwatch/named Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,827 +0,0 @@ -########################################################################## -# $Id$ -########################################################################## - -##################################################### -## Copyright (c) 2008 Kirk Bauer -## 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. -######################################################### - -use Logwatch ':ip'; - - -#$DoLookup = ValueOrDefault($ENV{'named_ip_lookup'}, 0); -$Debug = ValueOrDefault($ENV{'LOGWATCH_DEBUG'}, 0); -$Detail = ValueOrDefault($ENV{'LOGWATCH_DETAIL_LEVEL'}, 0); - -# Avoid "Use of uninitialized value" warning messages. -sub ValueOrDefault { - my ($value, $default) = @_; - return ($value ? $value : $default); -} - -if ( $Debug >= 5 ) { - print STDERR "\n\nDEBUG: Inside NAMED Filter \n\n"; - $DebugCounter = 1; -} - - -while (defined($ThisLine = <STDIN>)) { - if ( $Debug >= 30 ) { - print STDERR "DEBUG($DebugCounter): $ThisLine"; - $DebugCounter++; - } - - 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 - # typo fixed in 2004 release - ($ThisLine =~ /transfer(r)?ed serial/) or - ($ThisLine =~ /There may be a name server already running/) or - ($ThisLine =~ /exiting/) or - ($ThisLine =~ /running/) 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 - # Be sure to catch: transfer of 'zone' from IP#53: failed to connect: timed out - # not exact just triggers a full transfer - ($ThisLine =~ /transfer of .*: (IXFR|AXFR(|-style IXFR) (started|ended)|connected using|Transfer completed|failed while receiving responses: not exact)/) or - ($ThisLine =~ /Transfer status: success/) or - ($ThisLine =~ /using \d+ CPU/) or - ($ThisLine =~ /loading configuration/) or - ($ThisLine =~ /command channel listening/) or - ($ThisLine =~ /configuring command channel from/) or - ($ThisLine =~ /interface ignored/) or - ($ThisLine =~ /no IPv6 interfaces found/) or - ($ThisLine =~ /using \d+ UDP listeners per interface/) 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 \S+/) or - ($ThisLine =~ /Malformed response from/) or - ($ThisLine =~ /client .* response from Internet for .*/) or -# ($ThisLine =~ /client .+ query \(cache\) '.*' denied/) or - ($ThisLine =~ /client .+(?: \([^)]+\))?: 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/) or - ($ThisLine =~ /^ succeeded$/) or - ($ThisLine =~ /\*\*\* POKED TIMER \*\*\*/) or - # The message about the end of transfer is the interesting one - ($ThisLine =~ /: Transfer started./) or - ($ThisLine =~ /D-BUS service (disabled|enabled)./) or - ($ThisLine =~ /D-BUS dhcdbd subscription disabled./) or - ($ThisLine =~ /automatic empty zone/) or - ($ThisLine =~ /binding TCP socket: address in use/) or - ($ThisLine =~ /dbus_mgr initialization failed. D-BUS service is disabled./) or - ($ThisLine =~ /dbus_svc_add_filter failed/) or - ($ThisLine =~ /isc_log_open 'named.run' failed: permission denied/) or - ($ThisLine =~ /weak RSASHA1 \(5\) key found \(exponent=3\)/) or - ($ThisLine =~ /Bad file descriptor/) or - ($ThisLine =~ /open: .*: file not found/) or - ($ThisLine =~ /queries: client [\.0-9a-fA-F#:]* view localhost_resolver: query: .* IN .*/) or - ($ThisLine =~ /zone .*: NS '.*' is a CNAME \(illegal\)/) or - ($ThisLine =~ /skipping nameserver '.*' because it is a CNAME,/) or - ($ThisLine =~ /zone .*: zone serial unchanged. zone may fail to transfer to slaves/) or - ($ThisLine =~ /zone .*: loading from master file .* failed/) or - ($ThisLine =~ /zone .*: NS '.*' has no address records/) or - ($ThisLine =~ /.*: not a valid number$/) or - ($ThisLine =~ /^(.*: )?unexpected end of input/) or - ($ThisLine =~ /too many timeouts resolving '.*' .*: disabling EDNS/) or - ($ThisLine =~ /too many timeouts resolving '.*' .*: reducing the advertised EDNS UDP packet size to .* octets/) or - ($ThisLine =~ /reloading zones succeeded/) or - ($ThisLine =~ /generating session key/) or - ($ThisLine =~ /success resolving '.*' \(in '.*'?\) after disabling EDNS/) or - ($ThisLine =~ /success resolving '.*' \(in '.*'?\) after reducing the advertised EDNS UDP packet size to 512 octets/) or - ($ThisLine =~ /the working directory is not writable/) or - ($ThisLine =~ /using default UDP\/IPv[46] port range: \[[0-9]*, [0-9]*\]/) or - ($ThisLine =~ /adjusted limit on open files from [0-9]* to [0-9]*/) or - ($ThisLine =~ /using up to [0-9]* sockets/) or - ($ThisLine =~ /built with/) or - ($ThisLine =~ /TTL differs in rdataset, adjusting [0-9]* -> [0-9]*/) or - ($ThisLine =~ /max open files \([0-9]*\) is smaller than max sockets \([0-9]*\)/) or - ($ThisLine =~ /clients-per-query (?:de|in)creased to .*/) or - ($ThisLine =~ /^must-be-secure resolving '.*': .*/) or - ($ThisLine =~ /^(error \()?no valid (DS|KEY|RRSIG)\)? resolving '.*': .*/) or - ($ThisLine =~ /^not insecure resolving '.*': .*/) or - ($ThisLine =~ /^validating \@0x[[:xdigit:]]+: .* DS: must be secure failure/) or - ($ThisLine =~ /^(error \()?broken trust chain\)? resolving '.*': .*/) or - ($ThisLine =~ /journal file [^ ]* does not exist, creating it/) or - ($ThisLine =~ /serial number \(\d+\) received from master/) or - ($ThisLine =~ /zone .*: notify from .*: serial \d+/) or - ($ThisLine =~ /zone is up to date/) or - ($ThisLine =~ /refresh in progress, refresh check queued/) or - ($ThisLine =~ /refresh: NODATA response from master/) or - ($ThisLine =~ /update with no effect/) or - ($ThisLine =~ /reading built-in trusted keys from file/) or - ($ThisLine =~ /reading built-in trust anchors from file/) or - ($ThisLine =~ /using built-in trusted-keys/) or - ($ThisLine =~ /set up managed keys zone/) or - ($ThisLine =~ /managed-keys-zone.*key now trusted/) or - ($ThisLine =~ /dhcpupdate: forwarding update for zone/) or - ($ThisLine =~ /forwarded dynamic update: master [^ ]* returned: (NXRRSET|YXDOMAIN)/) or - ($ThisLine =~ /using .* as GeoIP directory/) or - ($ThisLine =~ /GEO-.* Build/) or - ($ThisLine =~ /initializing GeoIP /) or - # the following seems okay since it says "success" - ($ThisLine =~ /managed-keys-zone.*: No DNSKEY RRSIGs found for '.*': success/) or - ($ThisLine =~ /managed-keys-zone.*: Unable to fetch DNSKEY set '.*': timed out/) or - ($ThisLine =~ /^sizing zone task pool based on \d+ zones/) or - ($ThisLine =~ /^BIND \d+ is maintained by Internet Systems Consortium/) or - ($ThisLine =~ /a non-profit 501/) or - ($ThisLine =~ /corporation. Support and training for BIND \d+ are/) or - ($ThisLine =~ /available at https:\/\/www.isc.org\/support/) or - ($ThisLine =~ /----------------------------------------------------/) or - ($ThisLine =~ /next key event: /) or - ($ThisLine =~ /reconfiguring zone keys/) or - ($ThisLine =~ /using built-in DLV key/) or -# ($ThisLine =~ /reading built-in trusted keys from file/) or - ($ThisLine =~ /all zones loaded/) or - ($ThisLine =~ /resolver priming query complete/) or - ($ThisLine =~ /client .* signer .* approved/) or - ($ThisLine =~ /stop limiting/) or - # ignore this line because the following line describes the error - ($ThisLine =~ /unexpected error/) - ) { - # 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 ( $ThisLine =~ /named shutdown failed/ ) { - $ShutdownNamedFail++; - } elsif ( (($Host, $Zone) = ( $ThisLine =~ /client ([^\#]+)#[^\:]+: (?:view \w+: )?zone transfer '(.+)' denied/ )) or - (($Host, $Zone) = ( $ThisLine =~ /client ([^\#]+)#[^\:]+: (?:view \w+: )?bad zone transfer request: '(.+)':/ )) ) { - $DeniedZoneTransfers{$Host}{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /zone (.+) zone transfer deferred due to quota/ ) ) { - $DeferredZoneTransfers{$Zone}++; - } elsif ( ($Zone, $Host) = ( $ThisLine =~ /transfer of '(.+)' from ([^\#]+)#[^\:]+: (failed|(Transfer status|giving up): ((network|host) unreachable|timed out|connection refused))/ ) ) { - $FailedZoneTransfers{$Host}{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /cache zone \"(.*)\" loaded/ ) ) { - $ZoneLoaded{"cache $Zone"}++; - } elsif ( ($Zone) = ( $ThisLine =~ /cache zone \"(.*)\" .* loaded/ ) ) { - $ZoneLoaded{"cache $Zone"}++; - } elsif ( ($Zone) = ( $ThisLine =~ /automatic empty zone: (.*)/ ) ) { - $ZoneLoaded{"automatic empty zone $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 (.+): expired/ ) ) { - $ZoneExpired{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /zone (.+): loaded serial/ ) ) { - $ZoneLoaded{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /(managed-keys-zone.*): loaded serial/ ) ) { - $ZoneLoaded{$Zone}++; - } elsif ( (undef,$Addr,$Server) = ( $ThisLine =~ /(C|c)onnection refused\)? resolving '(.+)': (.+)/ ) ) { - $ConnectionRefused{$Addr}{$Server}++; - } elsif ( (undef,$Addr,undef,$Server) = ( $ThisLine =~ /ame server (on|resolving) '(.+)' \(in .+\):\s+(\[.+\]\.\d+)?\s*'?(.+)'?:?/ ) ) { - $LameServer{$Addr}{$Server}++; - } elsif ( (($Zone) = ( $ThisLine =~ /Zone \"(.+)\" was removed/ )) or - (($Zone) = ( $ThisLine =~ /zone (.+): \(.*\) removed/ )) ) { - $ZoneRemoved{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /received notify for zone '(.*)'/ ) ) { - $ZoneReceivedNotify{$Zone}++; - } elsif ( ($Zone) = ( $ThisLine =~ /zone (.+): refused notify from non-master/ ) ) { - $ZoneRefusedNotify{$Zone}++; -# } elsif ( ($Rhost,$Ldom,$Reason) = ( $ThisLine =~ /client ([\d\.a-fA-F:]+) bad zone transfer request: '(.+)': (.+)$/ ) ) { - } elsif ( ($Rhost,$Ldom,$Reason) = ( $ThisLine =~ /client ([\.0-9a-fA-F:]+)#\d+: bad zone transfer request: '(.+)': (.+)/ ) ) { - $BadZone{$Reason}{"$Rhost ($Ldom)"}++; - } 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 ( ($File,$Line,$Entry,$Error) = ( $ThisLine =~ /warning: ([^:]+):(\d+): (.+)$/ ) ) { - $ZoneFileErrors{$File}{"file does not end with newline: $Error"}++; - } elsif ( ($Way,$Host) = ( $ThisLine =~ /([^ ]+): sendto\(\[([^ ]+)\].+\): Network is unreachable/ ) ) { - $FullHost = LookupIP ($Host); - $NetworkUnreachable{$Way}{$FullHost}++; - } elsif ( ($Host,$Way) = ( $ThisLine =~ /client (?:\@0x[0-9a-fA-F]+ )?(.*)#\d+(?: \(.*\))?: (?:view \w+: )?error ([^ ]+) response: network unreachable/ ) ) { - $FullHost = LookupIP ($Host); - $NetworkUnreachable{$Way}{$FullHost}++; - } elsif ( ($Zone,$Message) = ( $ThisLine =~ /client [^\#]+#[^\:]+: (?:view \w+: )?updating zone '([^\:]+)': (.*)$/ ) ) { - $ZoneUpdates{$Zone}{$Message}++; - } elsif ( ($Host,$Zone) = ( $ThisLine =~ /approved AXFR from \[(.+)\]\..+ for \"(.+)\"/ ) ) { - $FullHost = LookupIP ($Host); - $AXFR{$Zone}{$FullHost}++; - } elsif ( ($Client) = ( $ThisLine =~ /warning: client (.*) no more TCP clients/ ) ) { - $FullClient = LookupIP ($Client); - $DeniedTCPClient{$FullClient}++; - } elsif ( ($Client) = ( $ThisLine =~ /client (?:\@0x[0-9a-fA-F]+ )?(.*)#\d+(?: \(.*\))?: (?:view \w+: )?query \(cache\) (?:'.*' )?denied/ ) ) { - $FullClient = LookupIP ($Client); - $DeniedQuery{$FullClient}++; - } elsif ( ($Client) = ( $ThisLine =~ /client (?:\@0x[0-9a-fA-F]+ )?([^#]*)(#\d+)?(?: \(.*\))?: query '.*' denied/ ) ) { - $FullClient = LookupIP ($Client); - $DeniedQueryNoCache{$FullClient}++; - } elsif ( ($Rhost, $ViewName, $Ldom) = ($ThisLine =~ /client (?:\@0x[0-9a-fA-F]+ )?([\.0-9a-fA-F:]+)#\d+: (?:view (\w+): )?update '(.*)' denied/)) { - $ViewName = ($ViewName ? "/$ViewName" : ""); - $UpdateDenied{"$Rhost ($Ldom$ViewName)"}++; - } elsif ( ($Rhost, $Ldom) = ($ThisLine =~ /client ([\d\.]+)#\d+: update forwarding '(.*)' denied/)) { - $UpdateForwardingDenied{"$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 ( ($Zone) = ($ThisLine =~ /(managed-keys-zone.*): journal file is out of date: removing journal file/)) { - $JournalFail{$Zone}++; - } elsif ( ($Channel,$Reason) = ($ThisLine =~ /couldn't add command channel (.+#\d+): (.*)$/)) { - $ChannelAddFail{$Channel}{$Reason}++; - } elsif ( ($Zone,$Host,undef,$Reason) = ($ThisLine =~ /zone ([^ ]*): refresh: failure trying master ([^ ]*)#\d+( \(source .*\))?: (.*)/) ) { - $MasterFailure{"$Zone from $Host"}{$Reason}++; - } elsif ( ($Zone,$Reason,$Host) = ($ThisLine =~ /zone ([^ ]*): refresh: unexpected rcode \((.*)\) from master ([^ ]*)#\d+/) ) { - $MasterFailure{"$Zone from $Host"}{$Reason}++; - } elsif ( ($Zone) = ($ThisLine =~ /zone ([^\/]+)\/.+: refresh: non-authoritative answer from master/)) { - $NonAuthoritative{$Zone}++; - } elsif ( ($Zone) = ($ThisLine =~ /zone ([^\/]+)\/.+: refresh: retry limit for master \S+ exceeded/) ) { - $RetryLimit{$Zone}++; - } elsif ( ($Rcode, $Zone, $Host) = ($ThisLine =~ /(?:error \()?unexpected RCODE\)? \(?(.*?)\)? resolving '(.*)': (.*)$/) ){ - $UnexpRCODE{$Rcode}{$Zone}{$Host}++; - } elsif ( ($Rcode, $Zone, $Host) = ($ThisLine =~ /(.*) unexpected RCODE resolving '(.*)': (.*)$/) ){ - $UnexpRCODE{$Rcode}{$Zone}{$Host}++; - } elsif ( ($ThisLine =~ /(?:error \()?FORMERR\)? resolving '[^ ]+: [.0-9a-fA-F:#]+/) or - ($ThisLine =~ /DNS format error from [^ ]+ resolving [^ ]+( for client [^ ]+)?: .*/) ) { - chomp($ThisLine); - $FormErr{$ThisLine}++; - } elsif ( ($ThisLine =~ /found [0-9]* CPU(s)?, using [0-9]* worker thread(s)?/) ) { - chomp($ThisLine); - $StartLog{$ThisLine}++; - } elsif ( (($File,$Line,$Problem) = ($ThisLine =~ /\/etc\/(rndc.key|named.conf):([0-9]+): (unknown option '[^ ]*')/)) or - (($File,$Line,$Problem) = ($ThisLine =~ /\/etc\/(rndc.key|named.conf):([0-9]+): ('[^ ]' expected near end of file)/)) or - (($File,$Line,$Problem) = ($ThisLine =~ /\/etc\/(named.*.conf):([0-9]+): (.*)/)) or - (($File,$Line,$Problem) = ($ThisLine =~ /()()(could not configure root hints from '.*': file not found)/))) { - $ConfProb{$File}{"$Line,$Problem"}++; - } elsif ( (($ErrorText) = ($ThisLine =~ /^(RUNTIME_CHECK.*)/))or - (($ErrorText) = ($ThisLine =~ /^(.* REQUIRE.* failed.*)$/)) or - (($ErrorText) = ($ThisLine =~ /(.*: fatal error)/)) or - (($ErrorText) = ($ThisLine =~ /(.*: out of memory)/)) ) { - $NError{$ErrorText}++; - } elsif ( (($ErrorText) = ($ThisLine =~ /^(GeoIP .* DB not available)/)) ) { - $GeoIPError{$ErrorText}++; - } elsif ( (($ErrorText) = ($ThisLine =~ /^(internal_accept: fcntl\(\) failed: Too many open files)/)) or - (($ErrorText) = ($ThisLine =~ /^(socket: too many open file descriptors)/)) ) { - $ErrOpenFiles{$ErrorText}++; - } elsif ( ($From,$Log) = ($ThisLine =~ /invalid command from ([\.0-9a-fA-F:]*)#[0-9]*: (.*)/) ) { - $CCMessages{"$From,$Log"}++; - } elsif ( (($Log) = ($ThisLine =~ /(freezing .*zone.*)/)) or - (($Log) = ($ThisLine =~ /(thawing .*zone.*)/)) ) { - $CCMessages2{$Log}++; - } elsif (($CCC) = ($ThisLine =~ /unknown control channel command '(.*)'/)) { - $UnknownCCCommands{$CCC}++; - } elsif (($CCC) = ($ThisLine =~ /received control channel command '(.*)'/)) { - $CCCommands{$CCC}++; - } elsif (($Name,$Address) = ($ThisLine =~ /(?:error \()?network unreachable\)? resolving '(.*)': (.*)/)) { - $NUR{$Name}{$Address}++; - } elsif (($Name,$Address) = ($ThisLine =~ /(?:error \()?host unreachable\)? resolving '(.*)': (.*)/)) { - $HUR{$Name}{$Address}++; - } elsif (($Client) = ($ThisLine =~ /client ([\da-fA-F.:]+)(?:#\d*:)? notify question section contains no SOA/)) { - $NoSOA{$Client}++; - } elsif (($Hint) = ($ThisLine =~ /checkhints: (.*)/) ) { - $Hints{$Hint}++; - } elsif (($Response,$Net,$Zone) = ($ThisLine =~/limit (.+) responses to (\S+)(?: for (.+) \()?/)) { - $Zone = "None" unless defined($Zone); - $Limit{$Zone}{$Response}{$Net}++; - } elsif (($Client,$Response,$Net,$Zone) = ($ThisLine =~/client ([^#]+)(?:#\d+)? \(.*\): (?:view \w+: )?rate limit drop (.+) response to (\S+)(?: for (\S+))?/)) { - $Zone = "None" unless defined($Zone); - $LimitDrop{$Zone}{$Response}{$Net}{$Client}++; - } elsif (($Client,$Response,$Net,$Zone) = ($ThisLine =~/client ([^#]+)(?:#\d+)? \(.*\): (?:view \w+: )?rate limit slip (.+) response to (\S+)(?: for (\S+))?/)) { - $Zone = "None" unless defined($Zone); - $LimitSlip{$Zone}{$Response}{$Net}{$Client}++; - } elsif (($Net,$Zone,$Response) = ($ThisLine =~/limit responses to (\S+)(?: for (\S+))? (.*) +\(/)) { - $Zone = "None" unless defined($Zone); - $Limit{$Zone}{$Response}{$Net}++; - } elsif (($Client,$Net,$Zone,$Response) = ($ThisLine =~/client ([^#]+)(?:#\d+)? \(.*\): (?:view \w+: )?rate limit drop response to (\S+)(?: for (\S+))? (.*) +\(/)) { - $Zone = "None" unless defined($Zone); - $LimitDrop{$Zone}{$Response}{$Net}{$Client}++; - } elsif (($Client,$Net,$Zone,$Response) = ($ThisLine =~/client ([^#]+)(?:#\d+)? \(.*\): (?:view \w+: )?rate limit slip response to (\S+)(?: for (\S+))? (.*) +\(/)) { - $Zone = "None" unless defined($Zone); - $LimitSlip{$Zone}{$Response}{$Net}{$Client}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating \@0x[[:xdigit:]]+: (.*) (\w+): got insecure response; parent indicates it should be secure/)) { - $DNSSECInsec{'__Total__'}++; - $DNSSECInsec{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating \@0x[[:xdigit:]]+: (.*) (\w+): no valid signature found/)) { - $DNSSECInvalid{'__Total__'}++; - $DNSSECInvalid{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating \@0x[[:xdigit:]]+: (.*) (\w+): bad cache hit/)) { - $DNSSECBadCache{'__Total__'}++; - $DNSSECBadCache{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating \@0x[[:xdigit:]]+: (.*) (\w+): verify failed due to bad signature/)) { - $DNSSECInvalid{'__Total__'}++; - $DNSSECInvalid{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating ([^\/]*)\/(\w+): got insecure response; parent indicates it should be secure/)) { - $DNSSECInsec{'__Total__'}++; - $DNSSECInsec{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating ([^\/]*)\/(\w+): no valid signature found/)) { - $DNSSECInvalid{'__Total__'}++; - $DNSSECInvalid{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating ([^\/]*)\/(\w+): verify failed due to bad signature/)) { - $DNSSECInvalid{'__Total__'}++; - $DNSSECInvalid{$Zone}{$RR}++; - } elsif (($Zone,$RR) = ($ThisLine =~ /^\s*validating ([^\/]*)\/(\w+): bad cache hit/)) { - $DNSSECBadCache{'__Total__'}++; - $DNSSECBadCache{$Zone}{$RR}++; - } elsif (($Error,$Host) = ($ThisLine =~ /^(?:error \()?(.*)\)? resolving '([^']+)':/)) { - $DNSSECError{$Error}{'__Total__'}++; - $DNSSECError{$Error}{$Host}++; - } elsif ($ThisLine =~ /^samba_dlz:/) { - if ( ($Rhost, $Error) = ($ThisLine =~ /disallowing update of signer=.* name=(.*) type=.* error=(.*)/ )) { - $UpdateDenied{"$Rhost ($Error)"}++; - } - # ignore rest of samba4 dlz entries for now - } else { - # Report any unmatched entries... - # remove PID from named messages - $ThisLine =~ s/(client [\.0-9a-fA-F:]+)\S+/$1/; - chomp($ThisLine); - $OtherList{$ThisLine}++; - } -} - -####################################### - -if ( keys %ZoneExpired ) { - print "\nZones expired:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %ZoneExpired) { - print " $ThisOne: $ZoneExpired{$ThisOne} Time(s)\n"; - } -} - -if ( keys %FailedZoneTransfers ) { - print "\nFailed Zone Transfers:\n"; - foreach my $Host (keys %FailedZoneTransfers) { - print " $Host:\n"; - foreach my $Zone (keys %{$FailedZoneTransfers{$Host}}) { - print " $Zone: $FailedZoneTransfers{$Host}{$Zone} Time(s)\n"; - } - } -} - -if ( keys %DeniedZoneTransfers ) { - print "\nDenied Zone Transfers:\n"; - foreach my $Host (keys %DeniedZoneTransfers) { - print " $Host:\n"; - foreach my $Zone (keys %{$DeniedZoneTransfers{$Host}}) { - print " $Zone: $DeniedZoneTransfers{$Host}{$Zone} 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 %UpdateForwardingDenied ) { - print "\nZone update forwarding refused:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %UpdateForwardingDenied) { - print " $ThisOne: $UpdateForwardingDenied{$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 "\nJournal update failed:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %JournalFail) { - print " " . $ThisOne . ": " . $JournalFail{$ThisOne} . " Time(s)\n"; - } -} - -if (keys %ConfProb) { - print "\nErrors in configuration files\n"; - foreach $File (sort keys %ConfProb) { - if ($File =~ /.+/) { - print " file " . $File . "\n"; - foreach (keys %{$ConfProb{$File}}) { - ($Line,$Problem) = split ","; - print " " . $File . ":" . "$Line" . ": " . $Problem . ": " . $ConfProb{$File}{"$Line,$Problem"} . " Time(s)\n"; - } - } - else { - foreach (keys %{$ConfProb{$File}}) { - ($Line,$Problem) = split ","; - print " " . $Problem . ": " . $ConfProb{$File}{"$Line,$Problem"} . " Time(s)\n"; - } - } - } -} - -if (keys %NError) { - print "\nErrors:\n"; - foreach $ThisOne (keys %NError) { - print " " . $ThisOne . ": " . $NError{$ThisOne} . " Time(s)\n"; - } -} - -if (keys %ErrOpenFiles) { - print "\nThe following seams to be caused by the patches for CVE-2008-1447."; - print "\nPlease update your bind.\n"; - foreach $ThisOne (keys %ErrOpenFiles) { - print " " . $ThisOne . ": " . $ErrOpenFiles{$ThisOne} . " Time(s)\n"; - } -} - -if (keys %Limit) { - print "\nRate Limiting occurred for:\n"; - foreach $Zone (keys %Limit) { - print " $Zone:\n"; - foreach $Response (keys %{$Limit{$Zone}}) { - print " $Response:\n"; - foreach $Net (keys %{$Limit{$Zone}{$Response}}) { - print " $Net: $Limit{$Zone}{$Response}{$Net} Time(s)\n"; - foreach $Client (keys %{$LimitDrop{$Zone}{$Response}{$Net}}) { - print " Dropped $Client: $LimitDrop{$Zone}{$Response}{$Net}{$Client} Time(s)\n"; - } - foreach $Client (keys %{$LimitSlip{$Zone}{$Response}{$Net}}) { - print " Slipped $Client: $LimitSlip{$Zone}{$Response}{$Net}{$Client} Time(s)\n"; - } - } - } - } -} - -if ( ( $Detail >= 5 ) and (keys %GeoIPError) ) { - print "\nGeoIP Errors:\n"; - foreach $ThisOne (keys %GeoIPError) { - print " " . $ThisOne . ": " . $GeoIPError{$ThisOne} . " Time(s)\n"; - } -} - -if ((keys %CCMessages) or (keys %CCMessages2)){ - print "\nMessages from control channel\n"; - foreach (keys %CCMessages) { - ($From,$Log) = split ","; - print " " . $From . ": " . $Log . ": " . $CCMessages{"$From,$Log"} . " Time(s)\n"; - } - foreach $ThisOne (keys %CCMessages2) { - print " " . $ThisOne . ": " . $CCMessages2{$ThisOne} . " Time(s)\n"; - } -} - -if ( ( $Detail >= 5 ) and ($StartNamed) ) { - print "\nNamed 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 ($ShutdownNamedFail) ) { - print "Named shutdown failed: $ShutdownNamedFail 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 %ZoneReceivedNotify) ) { - print "\nZones receiving notify:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %ZoneReceivedNotify) { - print " $ThisOne: $ZoneReceivedNotify{$ThisOne} Time(s)\n"; - } -} - -if ( ( $Detail >= 5 ) and (keys %ZoneRefusedNotify) ) { - print "\nZones refused notify:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %ZoneRefusedNotify) { - print " $ThisOne: $ZoneRefusedNotify{$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 %DeferredZoneTransfers) ) { - print "\nDeferred Zone Transfers:\n"; - foreach my $Zone (keys %DeferredZoneTransfers) { - print " $Zone: $DeferredZoneTransfers{$Zone} Time(s)\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 >= 10 ) and (keys %BadZone) ) { - print "\nBad Zone Transfer Request:\n"; - foreach $Reason (keys %BadZone) { - print " Reason: $Reason\n"; - foreach $ThisOne (sort {$a cmp $b} (keys %{$BadZone{$Reason}}) ) { - print " $ThisOne: $BadZone{$Reason}{$ThisOne} Time(s)\n"; - } - } -} - -if ( ( $Detail >= 5 ) and (keys %DeniedTCPClient) ) { - print "\nno more TCP clients warning:\n"; - foreach $ThisOne (keys %DeniedTCPClient) { - print " from $ThisOne: $DeniedTCPClient{$ThisOne} Time(s)\n"; - } -} - -if ( ( $Detail >= 5 ) and (keys %DeniedQuery) ) { - print "\nQueries (cached) that were denied:\n"; - foreach $ThisOne (keys %DeniedQuery) { - print " from $ThisOne: $DeniedQuery{$ThisOne} Time(s)\n"; - } -} - -if ( ( $Detail >= 10 ) and (keys %DeniedQueryNoCache) ) { - print "\nQueries (not cached) that were denied:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %DeniedQueryNoCache) { - print " from $ThisOne: $DeniedQueryNoCache{$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 %ConnectionRefused) ) { - print "\nConnection refused resolving:\n"; - foreach $Addr (sort keys %ConnectionRefused) { - print " $Addr:\n"; - foreach $Server (sort SortIP keys %{$ConnectionRefused{$Addr}}) { - print " $Server: $ConnectionRefused{$Addr}{$Server} Time(s)\n"; - } - } -} - -if ( ( $Detail >= 10 ) and (keys %LameServer) ) { - print "\nThese addresses had lame server references:\n"; - foreach $Addr (sort keys %LameServer) { - print " $Addr:\n"; - foreach $Server (sort SortIP keys %{$LameServer{$Addr}}) { - print " $Server: $LameServer{$Addr}{$Server} 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 %RetryLimit) ) { - print "\nRetry limit exceeded for these zones:\n"; - foreach $Zone (sort {$a cmp $b} keys %RetryLimit) { - print " $Zone: $RetryLimit{$Zone} Time(s)\n"; - } -} - -if ( ($Detail >= 10) and (keys %NoSOA) ) { - print "\nNotify question sections of these clients contained no SOA:\n"; - foreach $Client (sort {$a cmp $b} keys %NoSOA) { - print " $Client: $NoSOA{$Client} 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 >= 10 ) and (keys %NUR) ) { - print "\nNetwork unreachable resolving for:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %NUR) { - print " $ThisOne:\n"; - foreach $Host (sort {$a cmp $b} keys %{$NUR{$ThisOne}}) { - print " $Host: $NUR{$ThisOne}{$Host} Time(s)\n"; - } - } -} - -if ( ( $Detail >= 10 ) and (keys %HUR) ) { - print "\nHost unreachable resolving for:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %HUR) { - print " $ThisOne:\n"; - foreach $Host (sort {$a cmp $b} keys %{$HUR{$ThisOne}}) { - print " $Host: $HUR{$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 (($Detail >= 5) and (keys %UnexpRCODE)) { - print "\nUnexpected DNS RCODEs:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %UnexpRCODE) { - print " " . $ThisOne . ":\n"; - foreach $Zone (sort {$a cmp $b} keys %{$UnexpRCODE{$ThisOne}}) { - print " " . $Zone . ":\n"; - foreach $Host (sort SortIP keys %{$UnexpRCODE{$ThisOne}{$Zone}}) { - print " " . $Host . ": " . $UnexpRCODE{$ThisOne}{$Zone}{$Host} . " Time(s)\n"; - } - } - } -} - -if (($Detail >= 5) and (keys %FormErr)) { - print "\nIncorrect response format:\n"; - foreach $ThisOne (keys %FormErr) { - print " " . $ThisOne . ": " . $FormErr{$ThisOne} . " Time(s)\n"; - } -} - -if (($Detail >= 10) and (keys %StartLog)) { - print "\nNamed startup logs:\n"; - foreach $ThisOne (keys %StartLog) { - print " " . $ThisOne . ": " . $StartLog{$ThisOne} . " Time(s)\n"; - } -} - -if (($Detail and (keys %CCCommands)) or (keys %UnknownCCCommands)) { - print "\nReceived control channel commands\n"; - foreach $ThisOne (keys %CCCommands) { - print " " . $ThisOne . ": " . $CCCommands{$ThisOne} . " Time(s)\n"; - } - foreach $ThisOne (keys %UnknownCCCommands) { - print " " . $ThisOne . "(unknown command): " . $CCCommands{$ThisOne} . " Time(s)\n"; - } -} - -if (keys %Hints) { - print "\nCheckhints:\n"; - foreach $ThisOne (sort {$a cmp $b} keys %Hints) { - print " " .$ThisOne .": $Hints{$ThisOne} Time(s)\n"; - } -} - -if (($Detail >= 5) and (keys %DNSSECInsec)) { - print "\nDNSSEC Insecure Responses: " . $DNSSECInsec{'__Total__'} . " Time(s)\n"; - foreach $Zone (sort keys %DNSSECInsec) { - if (($Detail >= 10) and ($Zone =~ /.+/) and ($Zone ne '__Total__')) { - foreach $RR (sort keys %{$DNSSECInsec{$Zone}}) { - print " " . "$Zone/$RR: " . $DNSSECInsec{$Zone}{$RR} . " Time(s)\n"; - } - } - } -} - -if (($Detail >= 5) and (keys %DNSSECInvalid)) { - print "\nDNSSEC No Valid Signature: " . $DNSSECInvalid{'__Total__'} . " Time(s)\n"; - foreach $Zone (sort keys %DNSSECInvalid) { - if (($Detail >= 10) and ($Zone =~ /.+/) and ($Zone ne '__Total__')) { - foreach $RR (sort keys %{$DNSSECInvalid{$Zone}}) { - print " " . "$Zone/$RR: " . $DNSSECInvalid{$Zone}{$RR} . " Time(s)\n"; - } - } - } -} - -if (($Detail >= 5) and (keys %DNSSECBadCache)) { - print "\nDNSSEC Bad Cache hit: " . $DNSSECBadCache{'__Total__'} . " Time(s)\n"; - foreach $Zone (sort keys %DNSSECBadCache) { - if (($Detail >= 10) and ($Zone =~ /.+/) and ($Zone ne '__Total__')) { - foreach $RR (sort keys %{$DNSSECBadCache{$Zone}}) { - print " " . "$Zone/$RR: " . $DNSSECBadCache{$Zone}{$RR} . " Time(s)\n"; - } - } - } -} - -if (($Detail >= 5) and (keys %DNSSECError)) { - print "\nDNSSEC Errors:\n"; - foreach $Error (sort keys %DNSSECError) { - print " $Error: " . $DNSSECError{$Error}{'__Total__'} . " Time(s)\n"; - if ($Detail >= 10) { - foreach $Host (sort keys %{$DNSSECError{$Error}}) { - print " " . "$Host: " . $DNSSECError{$Error}{$Host} . " Time(s)\n" unless ($Host eq '__Total__'); - } - } - } -} - -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 syntax=perl et -# Local Variables: -# mode: perl -# perl-indent-level: 3 -# indent-tabs-mode: nil -# End:
--- a/common/logwatch/postfix Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5292 +0,0 @@ -#!/usr/bin/perl - -########################################################################## -# Postfix-logwatch: written and maintained by: -# -# Mike "MrC" Cappella <mike (at) cappella (dot) us> -# http://logreporters.sourceforge.net/ -# -# Please send all comments, suggestions, bug reports regarding this -# program/module to the email address above. I will respond as quickly -# as possible. [MrC] -# -# Questions regarding the logwatch program itself should be directed to -# the logwatch project at: -# http://sourceforge.net/projects/logwatch/support -# -####################################################### -### All work since Dec 12, 2006 (logwatch CVS revision 1.28) -### Copyright (c) 2006-2012 Mike Cappella -### -### 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. -########################################################## - -########################################################################## -# The original postfix logwatch filter was written by -# Kenneth Porter, and has had many contributors over the years. -# -# CVS log removed: see Changes file for postfix-logwatch at -# http://logreporters.sourceforge.net/ -# or included with the standalone postfix-logwatch distribution -########################################################################## - -########################################################################## -# -# Test data included via inline comments starting with "#TD" -# - -#use Devel::Size qw(size total_size); - -package Logreporters; -use 5.008; -use strict; -use warnings; -no warnings "uninitialized"; -use re 'taint'; - -our $Version = '1.40.00'; -our $progname_prefix = 'postfix'; - -# Specifies the default configuration file for use in standalone mode. -my $config_file = "/usr/local/etc/${progname_prefix}-logwatch.conf"; - -# support postfix long (2.9+) or short queue ids -my $re_QID_s = qr/[A-Z\d]+/; -my $re_QID_l = qr/(?:NOQUEUE|[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ\d]+)/; -our $re_QID; - -our $re_DSN = qr/(?:(?:\d{3})?(?: ?\d\.\d\.\d)?)/; -our $re_DDD = qr/(?:(?:conn_use=\d+ )?delay=-?[\d.]+(?:, delays=[\d\/.]+)?(?:, dsn=[\d.]+)?)/; - -#MODULE: ../Logreporters/Utils.pm -package Logreporters::Utils; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.003'; - @ISA = qw(Exporter); - @EXPORT = qw(&formathost &get_percentiles &get_percentiles2 &get_frequencies &commify &unitize - &get_usable_sectvars &add_section &begin_section_group &end_section_group - &get_version &unique_list); - @EXPORT_OK = qw(&gen_test_log); -} - -use subs qw (@EXPORT @EXPORT_OK); - - -# Formats IP and hostname for even column spacing -# -sub formathost($ $) { - # $_[0] : hostip - # $_[1] : hostname; - - if (! $Logreporters::Config::Opts{'unknown'} and $_[1] eq 'unknown') { - return $_[0]; - } - - return sprintf "%-$Logreporters::Config::Opts{'ipaddr_width'}s %s", - $_[0] eq '' ? '*unknown' : $_[0], - $_[1] eq '' ? '*unknown' : lc $_[1]; -} - -# Add a new section to the end of a section table -# -sub add_section($$$$$;$) { - my $sref = shift; - die "Improperly specified Section entry: $_[0]" if !defined $_[3]; - - my $entry = { - CLASS => 'DATA', - NAME => $_[0], - DETAIL => $_[1], - FMT => $_[2], - TITLE => $_[3], - }; - $entry->{'DIVISOR'} = $_[4] if defined $_[4]; - push @$sref, $entry; -} - -{ -my $group_level = 0; - -# Begin a new section group. Groups can nest. -# -sub begin_section_group($;@) { - my $sref = shift; - my $group_name = shift; - my $entry = { - CLASS => 'GROUP_BEGIN', - NAME => $group_name, - LEVEL => ++$group_level, - HEADERS => [ @_ ], - }; - push @$sref, $entry; -} - -# Ends a section group. -# -sub end_section_group($;@) { - my $sref = shift; - my $group_name = shift; - my $entry = { - CLASS => 'GROUP_END', - NAME => $group_name, - LEVEL => --$group_level, - FOOTERS => [ @_ ], - }; - push @$sref, $entry; -} -} - -# Generate and return a list of section table entries or -# limiter key names, skipping any formatting entries. -# If 'namesonly' is set, limiter key names are returned, -# otherwise an array of section array records is returned. -sub get_usable_sectvars(\@ $) { - my ($sectref,$namesonly) = @_; - my (@sect_list, %unique_names); - - foreach my $sref (@$sectref) { - #print "get_usable_sectvars: $sref->{NAME}\n"; - next unless $sref->{CLASS} eq 'DATA'; - if ($namesonly) { - $unique_names{$sref->{NAME}} = 1; - } - else { - push @sect_list, $sref; - } - } - # return list of unique names - if ($namesonly) { - return keys %unique_names; - } - return @sect_list; -} - -# Print program and version info, preceeded by an optional string, and exit. -# -sub get_version() { - - print STDOUT "@_\n" if ($_[0]); - print STDOUT "$Logreporters::progname: $Logreporters::Version\n"; - exit 0; -} - - -# Returns a list of percentile values given a -# sorted array of numeric values. Uses the formula: -# -# r = 1 + (p(n-1)/100) = i + d (Excel method) -# -# r = rank -# p = desired percentile -# n = number of items -# i = integer part -# d = decimal part -# -# Arg1 is an array ref to the sorted series -# Arg2 is a list of percentiles to use - -sub get_percentiles(\@ @) { - my ($aref,@plist) = @_; - my ($n, $last, $r, $d, $i, @vals, $Yp); - - $last = $#$aref; - $n = $last + 1; - #printf "%6d" x $n . "\n", @{$aref}; - - #printf "n: %4d, last: %d\n", $n, $last; - foreach my $p (@plist) { - $r = 1 + ($p * ($n - 1) / 100.0); - $i = int ($r); # integer part - # domain: $i = 1 .. n - if ($i == $n) { - $Yp = $aref->[$last]; - } - elsif ($i == 0) { - $Yp = $aref->[0]; - print "CAN'T HAPPEN: $Yp\n"; - } - else { - $d = $r - $i; # decimal part - #p = Y[i] + d(Y[i+1] - Y[i]), but since we're 0 based, use i=i-1 - $Yp = $aref->[$i-1] + ($d * ($aref->[$i] - $aref->[$i-1])); - } - #printf "\np(%6.2f), r: %6.2f, i: %6d, d: %6.2f, Yp: %6d", $p, $r, $i, $d, $Yp; - push @vals, $Yp; - } - - return @vals; -} - -sub get_num_scores($) { - my $scoretab_r = shift; - - my $totalscores = 0; - - for (my $i = 0; $i < @$scoretab_r; $i += 2) { - $totalscores += $scoretab_r->[$i+1] - } - - return $totalscores; -} - -# scoretab -# -# (score1, n1), (score2, n2), ... (scoreN, nN) -# $i $i+1 -# -# scores are 0 based (0 = 1st score) -sub get_nth_score($ $) { - my ($scoretab_r, $n) = @_; - - my $i = 0; - my $n_cur_scores = 0; - #print "Byscore (", .5 * @$scoretab_r, "): "; for (my $i = 0; $i < $#$scoretab_r / 2; $i++) { printf "%9s (%d) ", $scoretab_r->[$i], $scoretab_r->[$i+1]; } ; print "\n"; - - while ($i < $#$scoretab_r) { - #print "Samples_seen: $n_cur_scores\n"; - $n_cur_scores += $scoretab_r->[$i+1]; - if ($n_cur_scores >= $n) { - #printf "range: %s %s %s\n", $i >= 2 ? $scoretab_r->[$i - 2] : '<begin>', $scoretab_r->[$i], $i+2 > $#$scoretab_r ? '<end>' : $scoretab_r->[$i + 2]; - #printf "n: $n, i: %8d, n_cur_scores: %8d, score: %d x %d hits\n", $i, $n_cur_scores, $scoretab_r->[$i], $scoretab_r->[$i+1]; - return $scoretab_r->[$i]; - } - - $i += 2; - } - print "returning last score $scoretab_r->[$i]\n"; - return $scoretab_r->[$i]; -} - -sub get_percentiles2(\@ @) { - my ($scoretab_r, @plist) = @_; - my ($n, $last, $r, $d, $i, @vals, $Yp); - - #$last = $#$scoretab_r - 1; - $n = get_num_scores($scoretab_r); - #printf "\n%6d" x $n . "\n", @{$scoretab_r}; - - #printf "\n\tn: %4d, @$scoretab_r\n", $n; - foreach my $p (@plist) { - ###print "\nPERCENTILE: $p\n"; - $r = 1 + ($p * ($n - 1) / 100.0); - $i = int ($r); # integer part - if ($i == $n) { - #print "last:\n"; - #$Yp = $scoretab_r->[$last]; - $Yp = get_nth_score($scoretab_r, $n); - } - elsif ($i == 0) { - #$Yp = $scoretab_r->[0]; - print "1st: CAN'T HAPPEN\n"; - $Yp = get_nth_score($scoretab_r, 1); - } - else { - $d = $r - $i; # decimal part - #p = Y[i] + d(Y[i+1] - Y[i]), but since we're 0 based, use i=i-1 - my $ithvalprev = get_nth_score($scoretab_r, $i); - my $ithval = get_nth_score($scoretab_r, $i+1); - $Yp = $ithvalprev + ($d * ($ithval - $ithvalprev)); - } - #printf "p(%6.2f), r: %6.2f, i: %6d, d: %6.2f, Yp: %6d\n", $p, $r, $i, $d, $Yp; - push @vals, $Yp; - } - - return @vals; -} - - - -# Returns a list of frequency distributions given an incrementally sorted -# set of sorted scores, and an incrementally sorted list of buckets -# -# Arg1 is an array ref to the sorted series -# Arg2 is a list of frequency buckets to use -sub get_frequencies(\@ @) { - my ($aref,@blist) = @_; - - my @vals = ( 0 ) x (@blist); - my @sorted_blist = sort { $a <=> $b } @blist; - my $bucket_index = 0; - -OUTER: foreach my $score (@$aref) { - #print "Score: $score\n"; - for my $i ($bucket_index .. @sorted_blist - 1) { - #print "\tTrying Bucket[$i]: $sorted_blist[$i]\n"; - if ($score > $sorted_blist[$i]) { - $bucket_index++; - } - else { - #printf "\t\tinto Bucket[%d]\n", $bucket_index; - $vals[$bucket_index]++; - next OUTER; - } - } - #printf "\t\tinto Bucket[%d]\n", $bucket_index - 1; - $vals[$bucket_index - 1]++; - } - - return @vals; -} - -# Inserts commas in numbers for easier readability -# -sub commify ($) { - return undef if ! defined ($_[0]); - - my $text = reverse $_[0]; - $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; - return scalar reverse $text; -} - -# Unitize a number, and return appropriate printf formatting string -# -sub unitize($ $) { - my ($num, $fmt) = @_; - my $kilobyte = 2**10; - my $megabyte = 2**20; - my $gigabyte = 2**30; - my $terabyte = 2**40; - - if ($num >= $terabyte) { - $num /= $terabyte; - $fmt .= '.3fT'; - } elsif ($num >= $gigabyte) { - $num /= $gigabyte; - $fmt .= '.3fG'; - } elsif ($num >= $megabyte) { - $num /= $megabyte; - $fmt .= '.3fM'; - } elsif ($num >= $kilobyte) { - $num /= $kilobyte; - $fmt .= '.3fK'; - } else { - $fmt .= 'd '; - } - - return ($num, $fmt); -} - -# Returns a sublist of the supplied list of elements in an unchanged order, -# where only the first occurrence of each defined element is retained -# and duplicates removed -# -# Borrowed from amavis 2.6.2 -# -sub unique_list(@) { - my ($r) = @_ == 1 && ref($_[0]) ? $_[0] : \@_; # accept list, or a list ref - my (%seen); - my (@unique) = grep { defined($_) && !$seen{$_}++ } @$r; - - return @unique; -} - -# Generate a test maillog file from the '#TD' test data lines -# The test data file is placed in /var/tmp/maillog.autogen -# -# arg1: "postfix" or "amavis" -# arg2: path to postfix-logwatch or amavis-logwatch from which to read '#TD' data -# -# Postfix TD syntax: -# TD<service><QID>(<count>) log entry -# -sub gen_test_log($) { - my $scriptpath = shift; - - my $toolname = $Logreporters::progname_prefix; - my $datafile = "/var/tmp/maillog-${toolname}.autogen"; - - die "gen_test_log: invalid toolname $toolname" if ($toolname !~ /^(postfix|amavis)$/); - - eval { - require Sys::Hostname; - require Fcntl; - } or die "Unable to create test data file: required module(s) not found\n$@"; - - my $syslogtime = localtime; - $syslogtime =~ s/^....(.*) \d{4}$/$1/; - - my ($hostname) = split /\./, Sys::Hostname::hostname(); - - # # avoid -T issues - # delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; - - my $flags = &Fcntl::O_CREAT|&Fcntl::O_WRONLY|&Fcntl::O_TRUNC; - sysopen(FH, $datafile, $flags) or die "Can't create test data file: $!"; - print "Generating test log data file from $scriptpath: $datafile\n"; - - my $id; - @ARGV = ($scriptpath); - if ($toolname eq 'postfix') { - my %services = ( - DEF => 'smtpd', - bQ => 'bounce', - cN => 'cleanup', - cQ => 'cleanup', - lQ => 'local', - m => 'master', - p => 'pickup', - pQ => 'pickup', - ppQ => 'pipe', - pfw => 'postfwd', - pg => 'postgrey', - pgQ => 'postgrey', - ps => 'postsuper', - qQ => 'qmgr', - s => 'smtp', - sQ => 'smtp', - sd => 'smtpd', - sdN => 'smtpd', - sdQ => 'smtpd', - spf => 'policy-spf', - vN => 'virtual', - vQ => 'virtual', - ); - $id = 'postfix/smtp[12345]'; - - while (<>) { - if (/^\s*#TD([a-zA-Z]*[NQ]?)(\d+)?(?:\(([^)]+)\))? (.*)$/) { - my ($service,$count,$qid,$line) = ($1, $2, $3, $4); - - #print "SERVICE: %s, QID: %s, COUNT: %s, line: %s\n", $service, $qid, $count, $line; - - if ($service eq '') { - $service = 'DEF'; - } - die ("No such service: \"$service\": line \"$_\"") if (!exists $services{$service}); - - $id = $services{$service} . '[123]'; - $id = 'postfix/' . $id unless $services{$service} eq 'postgrey'; - #print "searching for service: \"$service\"\n\tFound $id\n"; - if ($service =~ /N$/) { $id .= ': NOQUEUE'; } - elsif ($service =~ /Q$/) { $id .= $qid ? $qid : ': DEADBEEF'; } - - $line =~ s/ +/ /g; - $line =~ s/^ //g; - #print "$syslogtime $hostname $id: \"$line\"\n" x ($count ? $count : 1); - print FH "$syslogtime $hostname $id: $line\n" x ($count ? $count : 1); - } - } - } - else { #amavis - my %services = ( - DEF => 'amavis', - dcc => 'dccproc', - ); - while (<>) { - if (/^\s*#TD([a-z]*)(\d+)? (.*)$/) { - my ($service,$count,$line) = ($1, $2, $3); - if ($service eq '') { - $service = 'DEF'; - } - die ("No such service: \"$service\": line \"$_\"") if (!exists $services{$service}); - $id = $services{$service} . '[123]:'; - if ($services{$service} eq 'amavis') { - $id .= ' (9999-99)'; - } - print FH "$syslogtime $hostname $id $line\n" x ($count ? $count : 1) - } - } - } - - close FH or die "Can't close $datafile: $!"; -} - -1; - -#MODULE: ../Logreporters/Config.pm -package Logreporters::Config; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.002'; - @ISA = qw(Exporter); - @EXPORT = qw(&init_run_mode &add_option &get_options &init_cmdline &get_vars_from_file - &process_limiters &process_debug_opts &init_getopts_table_common &zero_opts - @Optspec %Opts %Configvars @Limiters %line_styles $fw1 $fw2 $sep1 $sep2 - &D_CONFIG &D_ARGS &D_VARS &D_TREE &D_SECT &D_UNMATCHED &D_TEST &D_ALL - ); -} - -use subs @EXPORT; - -our @Optspec = (); # options table used by Getopts - -our %Opts = (); # program-wide options -our %Configvars = (); # configuration file variables -our @Limiters; - -# Report separator characters and widths -our ($fw1,$fw2) = (22, 10); -our ($sep1,$sep2) = ('=', '-'); - -use Getopt::Long; - - -BEGIN { - import Logreporters::Utils qw(&get_usable_sectvars); -} - -our %line_styles = ( - truncate => 0, - wrap => 1, - full => 2, -); - -sub init_run_mode($); -sub confighash_to_cmdline(\%); -sub get_vars_from_file(\% $); -sub process_limiters(\@); -sub add_option(@); -sub get_options($); -sub init_getopts_table_common(@); -sub set_supplemental_reports($$); -# debug constants -sub D_CONFIG () { 1<<0 } -sub D_ARGS () { 1<<1 } -sub D_VARS () { 1<<2 } -sub D_TREE () { 1<<3 } -sub D_SECT () { 1<<4 } -sub D_UNMATCHED () { 1<<5 } - -sub D_TEST () { 1<<30 } -sub D_ALL () { 1<<31 } - -my %debug_words = ( - config => D_CONFIG, - args => D_ARGS, - vars => D_VARS, - tree => D_TREE, - sect => D_SECT, - unmatched => D_UNMATCHED, - - test => D_TEST, - all => 0xffffffff, -); - -# Clears %Opts hash and initializes basic running mode options in -# %Opts hash by setting keys: 'standalone', 'detail', and 'debug'. -# Call early. -# -sub init_run_mode($) { - my $config_file = shift; - $Opts{'debug'} = 0; - - # Logwatch passes a filter's options via environment variables. - # When running standalone (w/out logwatch), use command line options - $Opts{'standalone'} = exists ($ENV{LOGWATCH_DETAIL_LEVEL}) ? 0 : 1; - - # Show summary section by default - $Opts{'summary'} = 1; - - if ($Opts{'standalone'}) { - process_debug_opts($ENV{'LOGREPORTERS_DEBUG'}) if exists ($ENV{'LOGREPORTERS_DEBUG'}); - } - else { - $Opts{'detail'} = $ENV{'LOGWATCH_DETAIL_LEVEL'}; - # XXX - #process_debug_opts($ENV{'LOGWATCH_DEBUG'}) if exists ($ENV{'LOGWATCH_DEBUG'}); - } - - # first process --debug, --help, and --version options - add_option ('debug=s', sub { process_debug_opts($_[1]); 1}); - add_option ('version', sub { &Logreporters::Utils::get_version(); 1;}); - get_options(1); - - # now process --config_file, so that all config file vars are read first - add_option ('config_file|f=s', sub { get_vars_from_file(%Configvars, $_[1]); 1;}); - get_options(1); - - # if no config file vars were read - if ($Opts{'standalone'} and ! keys(%Configvars) and -f $config_file) { - print "Using default config file: $config_file\n" if $Opts{'debug'} & D_CONFIG; - get_vars_from_file(%Configvars, $config_file); - } -} - -sub get_options($) { - my $pass_through = shift; - #$SIG{__WARN__} = sub { print "*** $_[0]*** options error\n" }; - # ensure we're called after %Opts is initialized - die "get_options: program error: %Opts is emtpy" unless exists $Opts{'debug'}; - - my $p = new Getopt::Long::Parser; - - if ($pass_through) { - $p->configure(qw(pass_through permute)); - } - else { - $p->configure(qw(no_pass_through no_permute)); - } - #$p->configure(qw(debug)); - - if ($Opts{'debug'} & D_ARGS) { - print "\nget_options($pass_through): enter\n"; - printf "\tARGV(%d): ", scalar @ARGV; - print @ARGV, "\n"; - print "\t$_ ", defined $Opts{$_} ? "=> $Opts{$_}\n" : "\n" foreach sort keys %Opts; - } - - if ($p->getoptions(\%Opts, @Optspec) == 0) { - print STDERR "Use ${Logreporters::progname} --help for options\n"; - exit 1; - } - if ($Opts{'debug'} & D_ARGS) { - print "\t$_ ", defined $Opts{$_} ? "=> $Opts{$_}\n" : "\n" foreach sort keys %Opts; - printf "\tARGV(%d): ", scalar @ARGV; - print @ARGV, "\n"; - print "get_options: exit\n"; - } -} - -sub add_option(@) { - push @Optspec, @_; -} - -# untaint string, borrowed from amavisd-new -sub untaint($) { - no re 'taint'; - - my ($str); - if (defined($_[0])) { - local($1); # avoid Perl taint bug: tainted global $1 propagates taintedness - $str = $1 if $_[0] =~ /^(.*)$/; - } - - return $str; -} - -sub init_getopts_table_common(@) { - my @supplemental_reports = @_; - - print "init_getopts_table_common: enter\n" if $Opts{'debug'} & D_ARGS; - - add_option ('help', sub { print STDOUT Logreporters::usage(undef); exit 0 }); - add_option ('gen_test_log=s', sub { Logreporters::Utils::gen_test_log($_[1]); exit 0; }); - add_option ('detail=i'); - add_option ('nodetail', sub { - # __none__ will set all limiters to 0 in process_limiters - # since they are not known (Sections table is not yet built). - push @Limiters, '__none__'; - # 0 = disable supplemental_reports - set_supplemental_reports(0, \@supplemental_reports); - }); - add_option ('max_report_width=i'); - add_option ('summary!'); - add_option ('show_summary=i', sub { $Opts{'summary'} = $_[1]; 1; }); - # untaint ipaddr_width for use w/sprintf() in Perl v5.10 - add_option ('ipaddr_width=i', sub { $Opts{'ipaddr_width'} = untaint ($_[1]); 1; }); - - add_option ('sect_vars!'); - add_option ('show_sect_vars=i', sub { $Opts{'sect_vars'} = $_[1]; 1; }); - - add_option ('syslog_name=s'); - add_option ('wrap', sub { $Opts{'line_style'} = $line_styles{$_[0]}; 1; }); - add_option ('full', sub { $Opts{'line_style'} = $line_styles{$_[0]}; 1; }); - add_option ('truncate', sub { $Opts{'line_style'} = $line_styles{$_[0]}; 1; }); - add_option ('line_style=s', sub { - my $style = lc($_[1]); - my @list = grep (/^$style/, keys %line_styles); - if (! @list) { - print STDERR "Invalid line_style argument \"$_[1]\"\n"; - print STDERR "Option line_style argument must be one of \"wrap\", \"full\", or \"truncate\".\n"; - print STDERR "Use $Logreporters::progname --help for options\n"; - exit 1; - } - $Opts{'line_style'} = $line_styles{lc($list[0])}; - 1; - }); - - add_option ('limit|l=s', sub { - my ($limiter,$lspec) = split(/=/, $_[1]); - if (!defined $lspec) { - printf STDERR "Limiter \"%s\" requires value (ex. --limit %s=10)\n", $_[1],$_[1]; - exit 2; - } - foreach my $val (split(/(?:\s+|\s*,\s*)/, $lspec)) { - if ($val !~ /^\d+$/ and - $val !~ /^(\d*)\.(\d+)$/ and - $val !~ /^::(\d+)$/ and - $val !~ /^:(\d+):(\d+)?$/ and - $val !~ /^(\d+):(\d+)?:(\d+)?$/) - { - printf STDERR "Limiter value \"$val\" invalid in \"$limiter=$lspec\"\n"; - exit 2; - } - } - push @Limiters, lc $_[1]; - }); - - print "init_getopts_table_common: exit\n" if $Opts{'debug'} & D_ARGS; -} - -sub get_option_names() { - my (@ret, @tmp); - foreach (@Optspec) { - if (ref($_) eq '') { # process only the option names - my $spec = $_; - $spec =~ s/=.*$//; - $spec =~ s/([^|]+)\!$/$1|no$1/g; - @tmp = split /[|]/, $spec; - #print "PUSHING: @tmp\n"; - push @ret, @tmp; - } - } - return @ret; -} - -# Set values for the configuration variables passed via hashref. -# Variables are of the form ${progname_prefix}_KEYNAME. -# -# Because logwatch lowercases all config file entries, KEYNAME is -# case-insensitive. -# -sub init_cmdline() { - my ($href, $configvar, $value, $var); - - # logwatch passes all config vars via environment variables - $href = $Opts{'standalone'} ? \%Configvars : \%ENV; - - # XXX: this is cheeze: need a list of valid limiters, but since - # the Sections table is not built yet, we don't know what is - # a limiter and what is an option, as there is no distinction in - # variable names in the config file (perhaps this should be changed). - my @valid_option_names = get_option_names(); - die "Options table not yet set" if ! scalar @valid_option_names; - - print "confighash_to_cmdline: @valid_option_names\n" if $Opts{'debug'} & D_ARGS; - my @cmdline = (); - while (($configvar, $value) = each %$href) { - if ($configvar =~ s/^${Logreporters::progname_prefix}_//o) { - # distinguish level limiters from general options - # would be easier if limiters had a unique prefix - $configvar = lc $configvar; - my $ret = grep (/^$configvar$/i, @valid_option_names); - if ($ret == 0) { - print "\tLIMITER($ret): $configvar = $value\n" if $Opts{'debug'} & D_ARGS; - push @cmdline, '-l', "$configvar" . "=$value"; - } - else { - print "\tOPTION($ret): $configvar = $value\n" if $Opts{'debug'} & D_ARGS; - unshift @cmdline, $value if defined ($value); - unshift @cmdline, "--$configvar"; - } - } - } - unshift @ARGV, @cmdline; -} - -# Obtains the variables from a logwatch-style .conf file, for use -# in standalone mode. Returns an ENV-style hash of key/value pairs. -# -sub get_vars_from_file(\% $) { - my ($href, $file) = @_; - my ($var, $val); - - print "get_vars_from_file: enter: processing file: $file\n" if $Opts{'debug'} & D_CONFIG; - - my $message = undef; - my $ret = stat ($file); - if ($ret == 0) { $message = $!; } - elsif (! -r _) { $message = "Permission denied"; } - elsif ( -d _) { $message = "Is a directory"; } - elsif (! -f _) { $message = "Not a regular file"; } - - if ($message) { - print STDERR "Configuration file \"$file\": $message\n"; - exit 2; - } - - my $prog = $Logreporters::progname_prefix; - open FILE, '<', "$file" or die "unable to open configuration file $file: $!"; - while (<FILE>) { - chomp; - next if (/^\s*$/); # ignore all whitespace lines - next if (/^\*/); # ignore logwatch's *Service lines - next if (/^\s*#/); # ignore comment lines - if (/^\s*\$(${prog}_[^=\s]+)\s*=\s*"?([^"]+)"?$/o) { - ($var,$val) = ($1,$2); - if ($val =~ /^(?:no|false)$/i) { $val = 0; } - elsif ($val =~ /^(?:yes|true)$/i) { $val = 1; } - elsif ($val eq '') { $var =~ s/${prog}_/${prog}_no/; $val = undef; } - - print "\t\"$var\" => \"$val\"\n" if $Opts{'debug'} & D_CONFIG; - - $href->{$var} = $val; - } - } - close FILE or die "failed to close configuration handle for $file: $!"; - print "get_vars_from_file: exit\n" if $Opts{'debug'} & D_CONFIG; -} - -sub process_limiters(\@) { - my ($sectref) = @_; - - my ($limiter, $var, $val, @errors); - my @l = get_usable_sectvars(@$sectref, 1); - - if ($Opts{'debug'} & D_VARS) { - print "process_limiters: enter\n"; - print "\tLIMITERS: @Limiters\n"; - } - while ($limiter = shift @Limiters) { - my @matched = (); - - printf "\t%-30s ",$limiter if $Opts{'debug'} & D_VARS; - # disable all limiters when limiter is __none__: see 'nodetail' cmdline option - if ($limiter eq '__none__') { - $Opts{$_} = 0 foreach @l; - next; - } - - ($var,$val) = split /=/, $limiter; - - if ($val eq '') { - push @errors, "Limiter \"$var\" requires value (ex. --limit limiter=10)"; - next; - } - - # try exact match first, then abbreviated match next - if (scalar (@matched = grep(/^$var$/, @l)) == 1 or scalar (@matched = grep(/^$var/, @l)) == 1) { - $limiter = $matched[0]; # unabbreviate limiter - print "MATCH: $var: $limiter => $val\n" if $Opts{'debug'} & D_VARS; - # XXX move limiters into section hash entry... - $Opts{$limiter} = $val; - next; - } - print "matched=", scalar @matched, ": @matched\n" if $Opts{'debug'} & D_VARS; - - push @errors, "Limiter \"$var\" is " . (scalar @matched == 0 ? "invalid" : "ambiguous: @matched"); - } - print "\n" if $Opts{'debug'} & D_VARS; - - if (@errors) { - print STDERR "$_\n" foreach @errors; - exit 2; - } - - # Set the default value of 10 for each section if no limiter exists. - # This allows output for each section should there be no configuration - # file or missing limiter within the configuration file. - foreach (@l) { - $Opts{$_} = 10 unless exists $Opts{$_}; - } - - # Enable collection for each section if a limiter is non-zero. - foreach (@l) { - #print "L is: $_\n"; - #print "DETAIL: $Opts{'detail'}, OPTS: $Opts{$_}\n"; - $Logreporters::TreeData::Collecting{$_} = (($Opts{'detail'} >= 5) && $Opts{$_}) ? 1 : 0; - } - #print "OPTS: \n"; map { print "$_ => $Opts{$_}\n"} keys %Opts; - #print "COLLECTING: \n"; map { print "$_ => $Logreporters::TreeData::Collecting{$_}\n"} keys %Logreporters::TreeData::Collecting; -} - -# Enable/disable supplemental reports -# arg1: 0=off, 1=on -# arg2,...: list of supplemental report keywords -sub set_supplemental_reports($$) { - my ($onoff,$aref) = @_; - - $Opts{$_} = $onoff foreach (@$aref); -} - -sub process_debug_opts($) { - my $optstring = shift; - - my @errors = (); - foreach (split(/\s*,\s*/, $optstring)) { - my $word = lc $_; - my @matched = grep (/^$word/, keys %debug_words); - - if (scalar @matched == 1) { - $Opts{'debug'} |= $debug_words{$matched[0]}; - next; - } - - if (scalar @matched == 0) { - push @errors, "Unknown debug keyword \"$word\""; - } - else { # > 1 - push @errors, "Ambiguous debug keyword abbreviation \"$word\": (matches: @matched)"; - } - } - if (@errors) { - print STDERR "$_\n" foreach @errors; - print STDERR "Debug keywords: ", join (' ', sort keys %debug_words), "\n"; - exit 2; - } -} - -# Zero the options controlling level specs and those -# any others passed via Opts key. -# -# Zero the options controlling level specs in the -# Detailed section, and set all other report options -# to disabled. This makes it easy via command line to -# disable the entire summary section, and then re-enable -# one or more sections for specific reports. -# -# eg. progname --nodetail --limit forwarded=2 -# -sub zero_opts ($ @) { - my $sectref = shift; - # remaining args: list of Opts keys to zero - - map { $Opts{$_} = 0; print "zero_opts: $_ => 0\n" if $Opts{'debug'} & D_VARS;} @_; - map { $Opts{$_} = 0 } get_usable_sectvars(@$sectref, 1); -} - -1; - -#MODULE: ../Logreporters/TreeData.pm -package Logreporters::TreeData; - -use 5.008; -use strict; -use re 'taint'; -use warnings; -no warnings "uninitialized"; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.001'; - @ISA = qw(Exporter); - @EXPORT = qw(%Totals %Counts %Collecting $END_KEY); - @EXPORT_OK = qw(&printTree &buildTree); - -} - -use subs @EXPORT_OK; - -BEGIN { - import Logreporters::Config qw(%line_styles); -} - -# Totals and Counts are the log line accumulator hashes. -# Totals: maintains per-section grand total tallies for use in Summary section -# Counts: is a multi-level hash, which maintains per-level key totals. -our (%Totals, %Counts); - -# The Collecting hash determines which sections will be captured in -# the Counts hash. Counts are collected only if a section is enabled, -# and this hash obviates the need to test both existence and -# non-zero-ness of the Opts{'keyname'} (either of which cause capture). -# XXX The Opts hash could be used .... -our %Collecting = (); - -sub buildTree(\% $ $ $ $ $); -sub printTree($ $ $ $ $); -=pod -[ a:b:c, ... ] - -which would be interpreted as follows: - -a = show level a detail -b = show at most b items at this level -c = minimun count that will be shown -=cut - -sub printTree($ $ $ $ $) { - my ($treeref, $lspecsref, $line_style, $max_report_width, $debug) = @_; - my ($entry, $line); - my $cutlength = $max_report_width - 3; - - my $topn = 0; - foreach $entry (sort bycount @$treeref) { - ref($entry) ne "HASH" and die "Unexpected entry in tree: $entry\n"; - - #print "LEVEL: $entry->{LEVEL}, TOTAL: $entry->{TOTAL}, HASH: $entry, DATA: $entry->{DATA}\n"; - - # Once the top N lines have been printed, we're done - if ($lspecsref->[$entry->{LEVEL}]{topn}) { - if ($topn++ >= $lspecsref->[$entry->{LEVEL}]{topn} ) { - print ' ', ' ' x ($entry->{LEVEL} + 3), "...\n" - unless ($debug) and do { - $line = ' ' . ' ' x ($entry->{LEVEL} + 3) . '...'; - printf "%-130s L%d: topn reached(%d)\n", $line, $entry->{LEVEL} + 1, $lspecsref->[$entry->{LEVEL}]{topn}; - }; - last; - } - } - - # Once the item's count falls below the given threshold, we're done at this level - # unless a top N is specified, as threshold has lower priority than top N - elsif ($lspecsref->[$entry->{LEVEL}]{threshold}) { - if ($entry->{TOTAL} <= $lspecsref->[$entry->{LEVEL}]{threshold}) { - print ' ', ' ' x ($entry->{LEVEL} + 3), "...\n" - unless ($debug) and do { - $line = ' ' . (' ' x ($entry->{LEVEL} + 3)) . '...'; - printf "%-130s L%d: threshold reached(%d)\n", $line, $entry->{LEVEL} + 1, $lspecsref->[$entry->{LEVEL}]{threshold}; - }; - last; - } - } - - $line = sprintf "%8d%s%s", $entry->{TOTAL}, ' ' x ($entry->{LEVEL} + 2), $entry->{DATA}; - - if ($debug) { - printf "%-130s %-60s\n", $line, $entry->{DEBUG}; - } - - # line_style full, or lines < max_report_width - - #printf "MAX: $max_report_width, LEN: %d, CUTLEN $cutlength\n", length($line); - if ($line_style == $line_styles{'full'} or length($line) <= $max_report_width) { - print $line, "\n"; - } - elsif ($line_style == $line_styles{'truncate'}) { - print substr ($line,0,$cutlength), '...', "\n"; - } - elsif ($line_style == $line_styles{'wrap'}) { - my $leader = ' ' x 8 . ' ' x ($entry->{LEVEL} + 2); - print substr ($line, 0, $max_report_width, ''), "\n"; - while (length($line)) { - print $leader, substr ($line, 0, $max_report_width - length($leader), ''), "\n"; - } - } - else { - die ('unexpected line style'); - } - - printTree ($entry->{CHILDREF}, $lspecsref, $line_style, $max_report_width, $debug) if (exists $entry->{CHILDREF}); - } -} - -my $re_IP_strict = qr/\b(25[0-5]|2[0-4]\d|[01]?\d{1,2})\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})\b/; -# XXX optimize this using packed default sorting. Analysis shows speed isn't an issue though -sub bycount { - # Sort by totals, then IP address if one exists, and finally by data as a string - - local $SIG{__WARN__} = sub { print "*** PLEASE REPORT:\n*** $_[0]*** Unexpected: \"$a->{DATA}\", \"$b->{DATA}\"\n" }; - - $b->{TOTAL} <=> $a->{TOTAL} - - || - - pack('C4' => $a->{DATA} =~ /^$re_IP_strict/o) cmp pack('C4' => $b->{DATA} =~ /^$re_IP_strict/o) - - || - - $a->{DATA} cmp $b->{DATA} -} - -# -# Builds a tree of REC structures from the multi-key %Counts hashes -# -# Parameters: -# Hash: A multi-key hash, with keys being used as category headings, and leaf data -# being tallies for that set of keys -# Level: This current recursion level. Call with 0. -# -# Returns: -# Listref: A listref, where each item in the list is a rec record, described as: -# DATA: a string: a heading, or log data -# TOTAL: an integer: which is the subtotal of this item's children -# LEVEL: an integer > 0: representing this entry's level in the tree -# CHILDREF: a listref: references a list consisting of this node's children -# Total: The cummulative total of items found for a given invocation -# -# Use the special key variable $END_KEY, which is "\a\a" (two ASCII bell's) to end a, -# nested hash early, or the empty string '' may be used as the last key. - -our $END_KEY = "\a\a"; - -sub buildTree(\% $ $ $ $ $) { - my ($href, $max_level_section, $levspecref, $max_level_global, $recurs_level, $show_unique, $debug) = @_; - my ($subtotal, $childList, $rec); - - my @treeList = (); - my $total = 0; - - foreach my $item (sort keys %$href) { - if (ref($href->{$item}) eq "HASH") { - #print " " x ($recurs_level * 4), "HASH: LEVEL $recurs_level: Item: $item, type: \"", ref($href->{$item}), "\"\n"; - - ($subtotal, $childList) = buildTree (%{$href->{$item}}, $max_level_section, $levspecref, $max_level_global, $recurs_level + 1, $debug); - - if ($recurs_level < $max_level_global and $recurs_level < $max_level_section) { - # me + children - $rec = { - DATA => $item, - TOTAL => $subtotal, - LEVEL => $recurs_level, - CHILDREF => $childList, - }; - - if ($debug) { - $rec->{DEBUG} = sprintf "L%d: levelspecs: %2d/%2d/%2d/%2d, Count: %10d", - $recurs_level + 1, $max_level_global, $max_level_section, - $levspecref->[$recurs_level]{topn}, $levspecref->[$recurs_level]{threshold}, $subtotal; - } - push (@treeList, $rec); - } - } - else { - if ($item ne '' and $item ne $END_KEY and $recurs_level < $max_level_global and $recurs_level < $max_level_section) { - $rec = { - DATA => $item, - TOTAL => $href->{$item}, - LEVEL => $recurs_level, - #CHILDREF => undef, - }; - if ($debug) { - $rec->{DEBUG} = sprintf "L%d: levelspecs: %2d/%2d/%2d/%2d, Count: %10d", - $recurs_level, $max_level_global, $max_level_section, - $levspecref->[$recurs_level]{topn}, $levspecref->[$recurs_level]{threshold}, $href->{$item}; - } - push (@treeList, $rec); - } - $subtotal = $href->{$item}; - } - - $total += $subtotal; - } - - #print " " x ($recurs_level * 4), "LEVEL $recurs_level: Returning from recurs_level $recurs_level\n"; - - return ($total, \@treeList); -} - -1; - -#MODULE: ../Logreporters/RegEx.pm -package Logreporters::RegEx; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); -# @EXPORT = qw($re_IP); - @EXPORT_OK = qw(); -} - -# IPv4 and IPv6 -# See syntax in RFC 2821 IPv6-address-literal, -# eg. IPv6:2001:630:d0:f102:230:48ff:fe77:96e -#our $re_IP = '(?:(?:::(?:ffff:|FFFF:)?)?(?:\d{1,3}\.){3}\d{1,3}|(?:(?:IPv6:)?[\da-fA-F]{0,4}:){2}(?:[\da-fA-F]{0,4}:){0,5}[\da-fA-F]{0,4})'; - -# Modified from "dartware" case at http://forums.dartware.com/viewtopic.php?t=452# -#our $re_IP = qr/(?:(?:(?:(?:[\da-f]{1,4}:){7}(?:[\da-f]{1,4}|:))|(?:(?:[\da-f]{1,4}:){6}(?::[\da-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[\da-f]{1,4}:){5}(?:(?:(?::[\da-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[\da-f]{1,4}:){4}(?:(?:(?::[\da-f]{1,4}){1,3})|(?:(?::[\da-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[\da-f]{1,4}:){3}(?:(?:(?::[\da-f]{1,4}){1,4})|(?:(?::[\da-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[\da-f]{1,4}:){2}(?:(?:(?::[\da-f]{1,4}){1,5})|(?:(?::[\da-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[\da-f]{1,4}:){1}(?:(?:(?::[\da-f]{1,4}){1,6})|(?:(?::[\da-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[\da-f]{1,4}){1,7})|(?:(?::[\da-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?)|(?:(?:\d{1,3}\.){3}(?:\d{1,3}))/i; - -# IPv4 only -#our $re_IP = qr/(?:\d{1,3}\.){3}(?:\d{1,3})/; - -1; - -#MODULE: ../Logreporters/Reports.pm -package Logreporters::Reports; - -use 5.008; -use strict; -use re 'taint'; -use warnings; -no warnings "uninitialized"; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.002'; - @ISA = qw(Exporter); - @EXPORT = qw(&inc_unmatched &print_unmatched_report &print_percentiles_report2 - &print_summary_report &print_detail_report); - @EXPORT_OK = qw(); -} - -use subs @EXPORT_OK; - -BEGIN { - import Logreporters::Config qw(%Opts $fw1 $fw2 $sep1 $sep2 &D_UNMATCHED &D_TREE); - import Logreporters::Utils qw(&commify &unitize &get_percentiles &get_percentiles2); - import Logreporters::TreeData qw(%Totals %Counts &buildTree &printTree); -} - -my (%unmatched_list); - -our $origline; # unmodified log line, for error reporting and debug - -sub inc_unmatched($) { - my ($id) = @_; - $unmatched_list{$origline}++; - print "UNMATCHED($id): \"$origline\"\n" if $Opts{'debug'} & D_UNMATCHED; -} - -# Print unmatched lines -# -sub print_unmatched_report() { - return unless (keys %unmatched_list); - - print "\n\n**Unmatched Entries**\n"; - foreach my $line (sort {$unmatched_list{$b}<=>$unmatched_list{$a} } keys %unmatched_list) { - printf "%8d %s\n", $unmatched_list{$line}, $line; - } -} - -=pod - ****** Summary ******************************************************** - 2 Miscellaneous warnings - - 20621 Total messages scanned ---------------- 100.00% - 662.993M Total bytes scanned 695,198,092 - ======== ================================================ - - 19664 Ham ----------------------------------- 95.36% - 19630 Clean passed 95.19% - 34 Bad header passed 0.16% - - 942 Spam ---------------------------------- 4.57% - 514 Spam blocked 2.49% - 428 Spam discarded (no quarantine) 2.08% - - 15 Malware ------------------------------- 0.07% - 15 Malware blocked 0.07% - - - 1978 SpamAssassin bypassed - 18 Released from quarantine - 1982 Whitelisted - 3 Blacklisted - 12 MIME error - 51 Bad header (debug supplemental) - 28 Extra code modules loaded at runtime -=cut -# Prints the Summary report section -# -sub print_summary_report (\@) { - my ($sections) = @_; - my ($keyname,$cur_level); - my @lines; - - my $expand_header_footer = sub { - my $line = undef; - - foreach my $horf (@_) { - # print blank line if keyname is newline - if ($horf eq "\n") { - $line .= "\n"; - } - elsif (my ($sepchar) = ($horf =~ /^(.)$/o)) { - $line .= sprintf "%s %s\n", $sepchar x 8, $sepchar x 50; - } - else { - die "print_summary_report: unsupported header or footer type \"$horf\""; - } - } - return $line; - }; - - if ($Opts{'detail'} >= 5) { - my $header = "****** Summary "; - print $header, '*' x ($Opts{'max_report_width'} - length $header), "\n\n"; - } - - my @headers; - foreach my $sref (@$sections) { - # headers and separators - die "Unexpected Section $sref" if (ref($sref) ne 'HASH'); - - # Start of a new section group. - # Expand and save headers to output at end of section group. - if ($sref->{CLASS} eq 'GROUP_BEGIN') { - $cur_level = $sref->{LEVEL}; - $headers[$cur_level] = &$expand_header_footer(@{$sref->{HEADERS}}); - } - - elsif ($sref->{CLASS} eq 'GROUP_END') { - my $prev_level = $sref->{LEVEL}; - - # If this section had lines to output, tack on headers and footers, - # removing extraneous newlines. - if ($lines[$cur_level]) { - # squish multiple blank lines - if ($headers[$cur_level] and substr($headers[$cur_level],0,1) eq "\n") { - if ( ! defined $lines[$prev_level][-1] or $lines[$prev_level][-1] eq "\n") { - $headers[$cur_level] =~ s/^\n+//; - } - } - - push @{$lines[$prev_level]}, $headers[$cur_level] if $headers[$cur_level]; - push @{$lines[$prev_level]}, @{$lines[$cur_level]}; - my $f = &$expand_header_footer(@{$sref->{FOOTERS}}); - push @{$lines[$prev_level]}, $f if $f; - $lines[$cur_level] = undef; - } - - $headers[$cur_level] = undef; - $cur_level = $prev_level; - } - - elsif ($sref->{CLASS} eq 'DATA') { - # Totals data - $keyname = $sref->{NAME}; - if ($Totals{$keyname} > 0) { - my ($numfmt, $desc, $divisor) = ($sref->{FMT}, $sref->{TITLE}, $sref->{DIVISOR}); - - my $fmt = '%8'; - my $extra = ' %25s'; - my $total = $Totals{$keyname}; - - # Z format provides unitized or unaltered totals, as appropriate - if ($numfmt eq 'Z') { - ($total, $fmt) = unitize ($total, $fmt); - } - else { - $fmt .= "$numfmt "; - $extra = ''; - } - - if ($divisor and $$divisor) { - # XXX generalize this - if (ref ($desc) eq 'ARRAY') { - $desc = @$desc[0] . ' ' . @$desc[1] x (42 - 2 - length(@$desc[0])); - } - - push @{$lines[$cur_level]}, - sprintf "$fmt %-42s %6.2f%%\n", $total, $desc, - $$divisor == $Totals{$keyname} ? 100.00 : $Totals{$keyname} * 100 / $$divisor; - } - else { - push @{$lines[$cur_level]}, - sprintf "$fmt %-23s $extra\n", $total, $desc, commify ($Totals{$keyname}); - } - } - } - else { - die "print_summary_report: unexpected control..."; - } - } - print @{$lines[0]}; - print "\n"; -} - -# Prints the Detail report section -# -# Note: side affect; deletes each key in Totals/Counts -# after printout. Only the first instance of a key in -# the Section table will result in Detail output. -sub print_detail_report (\@) { - my ($sections) = @_; - my $header_printed = 0; - - return unless (keys %Counts); - -#use Devel::Size qw(size total_size); - - foreach my $sref ( @$sections ) { - next unless $sref->{CLASS} eq 'DATA'; - # only print detail for this section if DETAIL is enabled - # and there is something in $Counts{$keyname} - next unless $sref->{DETAIL}; - next unless exists $Counts{$sref->{NAME}}; - - my $keyname = $sref->{NAME}; - my $max_level = undef; - my $print_this_key = 0; - - my @levelspecs = (); - clear_level_specs($max_level, \@levelspecs); - if (exists $Opts{$keyname}) { - $max_level = create_level_specs($Opts{$keyname}, $Opts{'detail'}, \@levelspecs); - $print_this_key = 1 if ($max_level); - } - else { - $print_this_key = 1; - } - #print_level_specs($max_level,\@levelspecs); - - # at detail 5, print level 1, detail 6: level 2, ... - -#print STDERR "building: $keyname\n"; - my ($count, $treeref) = - buildTree (%{$Counts{$keyname}}, defined ($max_level) ? $max_level : 11, - \@levelspecs, $Opts{'detail'} - 4, 0, $Opts{'debug'} & D_TREE); - - if ($count > 0) { - if ($print_this_key) { - my $desc = $sref->{TITLE}; - $desc =~ s/^\s+//; - - if (! $header_printed) { - my $header = "****** Detail ($max_level) "; - print $header, '*' x ($Opts{'max_report_width'} - length $header), "\n"; - $header_printed = 1; - } - printf "\n%8d %s %s\n", $count, $desc, - $Opts{'sect_vars'} ? - ('-' x ($Opts{'max_report_width'} - 18 - length($desc) - length($keyname))) . " [ $keyname ] -" : - '-' x ($Opts{'max_report_width'} - 12 - length($desc)) - } - - printTree ($treeref, \@levelspecs, $Opts{'line_style'}, $Opts{'max_report_width'}, - $Opts{'debug'} & D_TREE); - } -#print STDERR "Total size Counts: ", total_size(\%Counts), "\n"; -#print STDERR "Total size Totals: ", total_size(\%Totals), "\n"; - $treeref = undef; - $Totals{$keyname} = undef; - delete $Totals{$keyname}; - delete $Counts{$keyname}; - } - #print "\n"; -} - -=pod - -Print out a standard percentiles report - - === Delivery Delays Percentiles =============================================================== - 0% 25% 50% 75% 90% 95% 98% 100% - ----------------------------------------------------------------------------------------------- - Before qmgr 0.01 0.70 1.40 45483.70 72773.08 81869.54 87327.42 90966.00 - In qmgr 0.00 0.00 0.00 0.01 0.01 0.01 0.01 0.01 - Conn setup 0.00 0.00 0.00 0.85 1.36 1.53 1.63 1.70 - Transmission 0.03 0.47 0.92 1.61 2.02 2.16 2.24 2.30 - Total 0.05 1.18 2.30 45486.15 72776.46 81873.23 87331.29 90970.00 - =============================================================================================== - - === Postgrey Delays Percentiles =========================================================== - 0% 25% 50% 75% 90% 95% 98% 100% - ------------------------------------------------------------------------------------------- - Postgrey 727.00 727.00 727.00 727.00 727.00 727.00 727.00 727.00 - =========================================================================================== - - tableref: - data table: ref to array of arrays, first cell is label, subsequent cells are data - title: - table's title - percentiles_str: - string of space or comma separated integers, which are the percentiles - calculated and output as table column data -=cut -sub print_percentiles_report2($$$) { - my ($tableref, $title, $percentiles_str) = @_; - - return unless @$tableref; - - my $myfw2 = $fw2 - 1; - my @percents = split /[ ,]/, $percentiles_str; - - # Calc y label width from the hash's keys. Each key is padded with the - # string "#: ", # where # is a single-digit sort index. - my $y_label_max_width = 0; - for (@$tableref) { - $y_label_max_width = length($_->[0]) if (length($_->[0]) > $y_label_max_width); - } - - # Titles row - my $col_titles_str = sprintf "%-${y_label_max_width}s" . "%${myfw2}s%%" x @percents , ' ', @percents; - my $table_width = length($col_titles_str); - - # Table header row - my $table_header_str = sprintf "%s %s ", $sep1 x 3, $title; - $table_header_str .= $sep1 x ($table_width - length($table_header_str)); - - print "\n", $table_header_str; - print "\n", $col_titles_str; - print "\n", $sep2 x $table_width; - - my (@p, @coldata, @xformed); - foreach (@$tableref) { - my ($title, $ref) = ($_->[0], $_->[1]); - #xxx my @sorted = sort { $a <=> $b } @{$_->[1]}; - - my @byscore = (); - - for my $bucket (sort { $a <=> $b } keys %$ref) { - #print "Key: $title: Bucket: $bucket = $ref->{$bucket}\n"; - # pairs: bucket (i.e. key), tally - push @byscore, $bucket, $ref->{$bucket}; - } - - - my @p = get_percentiles2 (@byscore, @percents); - printf "\n%-${y_label_max_width}s" . "%${fw2}.2f" x scalar (@p), $title, @p; - } - -=pod - foreach (@percents) { - #printf "\n%-${y_label_max_width}s" . "%${fw2}.2f" x scalar (@p), substr($title,3), @p; - printf "\n%3d%%", $title; - foreach my $val (@{shift @xformed}) { - my $unit; - if ($val > 1000) { - $unit = 's'; - $val /= 1000; - } - else { - $unit = ''; - } - printf "%${fw3}.2f%-2s", $val, $unit; - } - } -=cut - - print "\n", $sep1 x $table_width, "\n"; -} - -sub clear_level_specs($ $) { - my ($max_level,$lspecsref) = @_; - #print "Zeroing $max_level rows of levelspecs\n"; - $max_level = 0 if (not defined $max_level); - for my $x (0..$max_level) { - $lspecsref->[$x]{topn} = undef; - $lspecsref->[$x]{threshold} = undef; - } -} - -# topn = 0 means don't limit -# threshold = 0 means no min threshold -sub create_level_specs($ $ $) { - my ($optkey,$gdetail,$lspecref) = @_; - - return 0 if ($optkey eq "0"); - - my $max_level = $gdetail; # default to global detail level - my (@specsP1, @specsP2, @specsP3); - - #printf "create_level_specs: key: %s => \"%s\", max_level: %d\n", $optkey, $max_level; - - foreach my $sp (split /[\s,]+/, $optkey) { - #print "create_level_specs: SP: \"$sp\"\n"; - # original level specifier - if ($sp =~ /^\d+$/) { - $max_level = $sp; - #print "create_level_specs: max_level set: $max_level\n"; - } - # original level specifier + topn at level 1 - elsif ($sp =~ /^(\d*)\.(\d+)$/) { - if ($1) { $max_level = $1; } - else { $max_level = $gdetail; } # top n specified, but no max level - - # force top N at level 1 (zero based) - push @specsP1, { level => 0, topn => $2, threshold => 0 }; - } - # newer level specs - elsif ($sp =~ /^::(\d+)$/) { - push @specsP3, { level => undef, topn => 0, threshold => $1 }; - } - elsif ($sp =~ /^:(\d+):(\d+)?$/) { - push @specsP2, { level => undef, topn => $1, threshold => defined $2 ? $2 : 0 }; - } - elsif ($sp =~ /^(\d+):(\d+)?:(\d+)?$/) { - push @specsP1, { level => ($1 > 0 ? $1 - 1 : 0), topn => $2 ? $2 : 0, threshold => $3 ? $3 : 0 }; - } - else { - print STDERR "create_level_specs: unexpected levelspec ignored: \"$sp\"\n"; - } - } - - #foreach my $sp (@specsP3, @specsP2, @specsP1) { - # printf "Sorted specs: L%d, topn: %3d, threshold: %3d\n", $sp->{level}, $sp->{topn}, $sp->{threshold}; - #} - - my ($min, $max); - foreach my $sp ( @specsP3, @specsP2, @specsP1) { - ($min, $max) = (0, $max_level); - - if (defined $sp->{level}) { - $min = $max = $sp->{level}; - } - for my $level ($min..$max) { - #printf "create_level_specs: setting L%d, topn: %s, threshold: %s\n", $level, $sp->{topn}, $sp->{threshold}; - $lspecref->[$level]{topn} = $sp->{topn} if ($sp->{topn}); - $lspecref->[$level]{threshold} = $sp->{threshold} if ($sp->{threshold}); - } - } - - return $max_level; -} - -sub print_level_specs($ $) { - my ($max_level,$lspecref) = @_; - for my $level (0..$max_level) { - printf "LevelSpec Row %d: %3d %3d\n", $level, $lspecref->[$level]{topn}, $lspecref->[$level]{threshold}; - } -} - - -1; - -#MODULE: ../Logreporters/RFC3463.pm -package Logreporters::RFC3463; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); - @EXPORT = qw(&get_dsn_msg); -} - -use subs @EXPORT; - -#------------------------------------------------- -# Enhanced Mail System Status Codes (aka: extended status codes) -# -# RFC 3463 http://www.ietf.org/rfc/rfc3463.txt -# RFC 4954 http://www.ietf.org/rfc/rfc4954.txt -# -# Class.Subject.Detail -# -my %dsn_codes = ( - class => { - '2' => 'Success', - '4' => 'Transient failure', - '5' => 'Permanent failure', - }, - - subject => { - '0' => 'Other/Undefined status', - '1' => 'Addressing status', - '2' => 'Mailbox status', - '3' => 'Mail system status', - '4' => 'Network & routing status', - '5' => 'Mail delivery protocol status', - '6' => 'Message content/media status', - '7' => 'Security/policy status', - }, - - detail => { - '0.0' => 'Other undefined status', - '1.0' => 'Other address status', - '1.1' => 'Bad destination mailbox address', - '1.2' => 'Bad destination system address', - '1.3' => 'Bad destination mailbox address syntax', - '1.4' => 'Destination mailbox address ambiguous', - '1.5' => 'Destination mailbox address valid', - '1.6' => 'Mailbox has moved', - '1.7' => 'Bad sender\'s mailbox address syntax', - '1.8' => 'Bad sender\'s system address', - - '2.0' => 'Other/Undefined mailbox status', - '2.1' => 'Mailbox disabled, not accepting messages', - '2.2' => 'Mailbox full', - '2.3' => 'Message length exceeds administrative limit.', - '2.4' => 'Mailing list expansion problem', - - '3.0' => 'Other/Undefined mail system status', - '3.1' => 'Mail system full', - '3.2' => 'System not accepting network messages', - '3.3' => 'System not capable of selected features', - '3.4' => 'Message too big for system', - - '4.0' => 'Other/Undefined network or routing status', - '4.1' => 'No answer from host', - '4.2' => 'Bad connection', - '4.3' => 'Routing server failure', - '4.4' => 'Unable to route', - '4.5' => 'Network congestion', - '4.6' => 'Routing loop detected', - '4.7' => 'Delivery time expired', - - '5.0' => 'Other/Undefined protocol status', - '5.1' => 'Invalid command', - '5.2' => 'Syntax error', - '5.3' => 'Too many recipients', - '5.4' => 'Invalid command arguments', - '5.5' => 'Wrong protocol version', - '5.6' => 'Authentication Exchange line too long', - - '6.0' => 'Other/Undefined media error', - '6.1' => 'Media not supported', - '6.2' => 'Conversion required & prohibited', - '6.3' => 'Conversion required but not supported', - '6.4' => 'Conversion with loss performed', - '6.5' => 'Conversion failed', - - '7.0' => 'Other/Undefined security status', - '7.1' => 'Delivery not authorized, message refused', - '7.2' => 'Mailing list expansion prohibited', - '7.3' => 'Security conversion required but not possible', - '7.4' => 'Security features not supported', - '7.5' => 'Cryptographic failure', - '7.6' => 'Cryptographic algorithm not supported', - '7.7' => 'Message integrity failure', - }, - - # RFC 4954 - complete => { - '2.7.0' => 'Authentication succeeded', - '4.7.0' => 'Temporary authentication failure', - '4.7.12' => 'Password transition needed', - '5.7.0' => 'Authentication required', - '5.7.8' => 'Authentication credentials invalid', - '5.7.9' => 'Authentication mechanism too weak', - '5.7.11' => 'Encryption required for requested authentication mechanism', - }, -); - -# Returns an RFC 3463 DSN messages given a DSN code -# -sub get_dsn_msg ($) { - my $dsn = shift; - my ($msg, $class, $subject, $detail); - - return "*DSN unavailable" if ($dsn =~ /^$/); - - unless ($dsn =~ /^(\d)\.((\d{1,3})\.\d{1,3})$/) { - print "Error: not a DSN code $dsn\n"; - return "Invalid DSN"; - } - - $class = $1; $subject = $3; $detail = $2; - - #print "DSN: $dsn, Class: $class, Subject: $subject, Detail: $detail\n"; - - if (exists $dsn_codes{'class'}{$class}) { - $msg = $dsn_codes{'class'}{$class}; - } - if (exists $dsn_codes{'subject'}{$subject}) { - $msg .= ': ' . $dsn_codes{'subject'}{$subject}; - } - if (exists $dsn_codes{'complete'}{$dsn}) { - $msg .= ': ' . $dsn_codes{'complete'}{$dsn}; - } - elsif (exists $dsn_codes{'detail'}{$detail}) { - $msg .= ': ' . $dsn_codes{'detail'}{$detail}; - } - - #print "get_dsn_msg: $msg\n" if ($msg); - return $dsn . ': ' . $msg; -} - -1; - -#MODULE: ../Logreporters/PolicySPF.pm -package Logreporters::PolicySPF; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); - @EXPORT = qw(&postfix_policy_spf); -} - -use subs @EXPORT; - -BEGIN { - import Logreporters::TreeData qw(%Totals %Counts $END_KEY); - import Logreporters::Utils; - import Logreporters::Reports qw(&inc_unmatched); -} - -# Handle postfix/policy_spf entries -# -# Mail::SPF::Result -# Pass the SPF record designates the host to be allowed to send accept -# Fail the SPF record has designated the host as NOT being allowed to send reject -# SoftFail the SPF record has designated the host as NOT being allowed to send but is in transition accept but mark -# Neutral the SPF record specifies explicitly that nothing can be said about validity accept -# None the domain does not have an SPF record or the SPF record does not evaluate to a result accept -# PermError a permanent error has occured (eg. badly formatted SPF record) unspecified -# TempError a transient error has occured accept or reject - -sub postfix_policy_spf($) { - my $line = shift; - - if ( - $line =~ /^Attribute: / or - # handler sender_policy_framework: is decisive. - $line =~ /^handler [^:]+/ or - # decided action=REJECT Please see http://www.openspf.org/why.html?sender=jrzjcez%40telecomitalia.it&ip=81.178.62.236&receiver=protegate1.zmi.at - $line =~ /^decided action=/ or - - # pypolicyd-spf-0.7.1 - # - # Read line: "request=smtpd_access_policy" - # Found the end of entry - # Config: {'Mail_From_reject': 'Fail', 'PermError_reject': 'False', 'HELO_reject': 'SPF_Not_Pass', 'defaultSeedOnly': 1, 'debugLevel': 4, 'skip_addresses': '127.0.0.0/8,::ffff:127.0.0.0//104,::1//128', 'TempError_Defer': 'False'} - # spfcheck: pyspf result: "['Pass', 'sender SPF authorized', 'helo']" - # ERROR: Could not match line "#helo pass and mfrom none" - # Traceback (most recent call last): - # File "/usr/local/bin/policyd-spf", line 405, in <module> - # line = sys.stdin.readline() - # KeyboardInterrupt - $line =~ /^Read line: "/ or - $line =~ /^Found the end of entry$/ or - $line =~ /^Config: {/ or - $line =~ /^spfcheck: pyspf result/ or - $line =~ /^Starting$/ or - $line =~ /^Normal exit$/ or - $line =~ /^ERROR: Could not match line/ or - $line =~ /^Traceback / or - $line =~ /^KeyboardInterrupt/ or - $line =~ /^\s\s+/ - ) - { - #print "IGNORING...\n\tORIG: $::OrigLine\n"; - return - } - - # Keep policy-spf warnings in its section - if (my ($warn,$msg) = $line =~ /^warning: ([^:]+): (.*)$/) { - #TDspf warning: ignoring garbage: # No SPF - - $msg =~ s/^# ?//; - $Totals{'policyspf'}++; - $Counts{'policyspf'}{'*Warning'}{ucfirst $warn}{$msg}{$END_KEY}++ if ($Logreporters::TreeData::Collecting{'policyspf'}); - return; - } - - # pypolicyd-spf-0.7.1 - - # Fail; identity=helo; client-ip=192.168.0.1; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # Fail; identity=helo; client-ip=192.168.0.2; helo=example.com; envelope-from=<>; receiver=bogus@example.net - # Neutral; identity=helo; client-ip=192.168.0.3; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # None; identity=helo; client-ip=192.168.0.4; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # None; identity=helo; client-ip=192.168.0.5; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # None; identity=mailfrom; client-ip=192.168.0.1; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # None; identity=mailfrom; client-ip=192.168.0.2; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # Pass; identity=helo; client-ip=192.168.0.2; helo=example.com; envelope-from=<>; receiver=bogus@example.net - # Permerror; identity=helo; client-ip=192.168.0.4; helo=example.com; envelope-from=f@example.com; receiver=bogus2@example.net - # Softfail; identity=mailfrom; client-ip=192.168.0.6; helo=example.com; envelope-from=f@example.com; receiver=yahl@example.org - if ($line =~ /^(Pass|Fail|None|Neutral|Permerror|Softfail|Temperror); (.*)$/) { - my $result = $1; - my %params = $2 =~ /([-\w]+)=([^;]+)/g; - #$params{'s'} = '*unknown' unless $params{'s'}; - $Totals{'policyspf'}++; - if ($Logreporters::TreeData::Collecting{'policyspf'}) { - my ($id) = $params{'identity'}; - $id =~ s/mailfrom/envelope-from/; - - $Counts{'policyspf'}{'Policy Action'}{"SPF: $result"}{join(': ',$params{'identity'},$params{$id})}{$params{'client-ip'}}{$params{'receiver'}}++; - } - return; - } - elsif ($line =~ /^ERROR /) { - $line =~ s/^ERROR //; - $Totals{'warningsother'}++; return unless ($Logreporters::TreeData::Collecting{'warningsother'}); - $Counts{'warningsother'}{"$Logreporters::service_name: $line"}++; - return; - } - - # Strip QID if it exists, and trailing ": ", leaving just the message. - $line =~ s/^(?:$Logreporters::re_QID|): //; - - # other ignored - if ( - $line =~ /^SPF \S+ \(.+?\): .*$/ or - $line =~ /^Mail From/ or - $line =~ /^:HELO check failed/ or # log entry has no space after : - $line =~ /^testing:/ - ) - { - #TDspf testing: stripped sender=jrzjcez@telecomitalia.it, stripped rcpt=hengstberger@adv.at - # postfix-policyd-spf-perl-2.007 - #TDspf SPF pass (Mechanism 'ip4:10.0.0.2/22' matched): Envelope-from: foo@example.com - #TDspf SPF pass (Mechanism 'ip4:10.10.10.10' matched): Envelope-from: anyone@sample.net - #TDspf SPF pass (Mechanism 'ip4:10.10.10.10' matched): HELO/EHLO (Null Sender): mailout2.example.com - #TDspf SPF fail (Mechanism '-all' matched): HELO/EHLO: mailout1.example.com - #TDspf SPF none (No applicable sender policy available): Envelope-from: efrom@example.com - #TDspf SPF permerror (Included domain 'example.com' has no applicable sender policy): Envelope-from: efrom@example.com - #TDspf SPF permerror (Maximum DNS-interactive terms limit (10) exceeded): Envelope-from: efrom@example.com - #TDspf Mail From (sender) check failed - Mail::SPF->new(10.0.0.1, , test.DNSreport.com) failed: 'identity' option must not be empty - #TDspf HELO check failed - Mail::SPF->new(, , ) failed: Missing required 'identity' option - - #TDspf SPF not applicable to localhost connection - skipped check - - #print "IGNORING...\n\tLINE: $line\n\tORIG: \"$Logreporters::Reports::origline\"\n"; - return; - } - - my ($action, $domain, $ip, $message, $mechanism); - ($domain, $ip, $message, $mechanism) = ('*unknown', '*unknown', '', '*unavailable'); - #print "LINE: '$line'\n"; - - # postfix-policyd-spf-perl: http://www.openspf.org/Software - if ($line =~ /^Policy action=(.*)$/) { - $line = $1; - - #: Policy action=DUNNO - return if $line =~ /^DUNNO/; - # Policy action=PREPEND X-Comment: SPF not applicable to localhost connection - skipped check - return if $line =~ /^PREPEND X-Comment: SPF not applicable to localhost connection - skipped check$/; - - #print "LINE: '$line'\n"; - if ($line =~ /^DEFER_IF_PERMIT SPF-Result=\[?(.*?)\]?: (.*) of .*$/) { - my ($lookup,$message) = ($1,$2); - # Policy action=DEFER_IF_PERMIT SPF-Result=[10.0.0.1]: Time-out on DNS 'SPF' lookup of '[10.0.0.1]' - # Policy action=DEFER_IF_PERMIT SPF-Result=example.com: 'SERVFAIL' error on DNS 'SPF' lookup of 'example.com' - $message =~ s/^(.*?) on (DNS SPF lookup)$/$2: $1/; - $message =~ s/'//g; - $Totals{'policyspf'}++; - $Counts{'policyspf'}{'Policy Action'}{'defer_if_permit'}{$message}{$lookup}{$END_KEY}++ if ($Logreporters::TreeData::Collecting{'policyspf'}); - return; - } - - if ($line =~ /^550 Please see http:\/\/www\.openspf\.org\/Why\?(.*)$/) { - # Policy action=550 Please see http://www.openspf.org/Why?s=mfrom&id=from%40example.com&ip=10.0.0.1&r=example.net - # Policy action=550 Please see http://www.openspf.org/Why?s=helo;id=mailout03.example.com;ip=192.168.0.1;r=mx1.example.net - # Policy action=550 Please see http://www.openspf.org/Why?id=someone%40example.com&ip=10.0.0.1&receiver=vps.example.net - - my %params; - for (split /[&;]/, $1) { - my ($id,$val) = split /=/, $_; - $params{$id} = $val; - } - $params{'id'} =~ s/^.*%40//; - $params{'s'} = '*unknown' unless $params{'s'}; - #print "Please see...:\n\tMessage: $message\n\tIP: $ip\n\tDomain: $domain\n"; - $Totals{'policyspf'}++; - $Counts{'policyspf'}{'Policy Action'}{'550 reject'}{'See http://www.openspf.org/Why?...'}{$params{'s'}}{$params{'ip'}}{$params{'id'}}++ if ($Logreporters::TreeData::Collecting{'policyspf'}); - return; - } - - if ($line =~ /^[^:]+: (none|pass|fail|softfail|neutral|permerror|temperror) \((.+?)\) receiver=[^;]+(?:; (.*))?$/) { - # iehc is identity, envelope-from, helo, client-ip - my ($result,$message,$iehc,$subject) = ($1,$2,$3,undef); - my %params = (); - #TDspf Policy action=PREPEND Received-SPF: pass (bounces.example.com ... _spf.example.com: 10.0.0.1 is authorized to use 'from@bounces.example.com' in 'mfrom' identity (mechanism 'ip4:10.0.0.1/24' matched)) receiver=sample.net; identity=mfrom; envelope-from="from@bounces.example.com"; helo=out.example.com; client-ip=10.0.0.1 - - # Note: "identity=mailfrom" new in Mail::SPF version 2.006 Aug. 17 - #TDspf Policy action=PREPEND Received-SPF: pass (example.com: 10.0.0.1 is authorized to use 'from@example.com' in 'mfrom' identity (mechanism 'ip4:10.0.0.0/24' matched)) receiver=mx.example.com; identity=mailfrom; envelope-from="from@example.com"; helo=example.com; client-ip=10.0.0.1 - - #TDspf Policy action=PREPEND Received-SPF: none (example.com: No applicable sender policy available) receiver=sample.net; identity=mfrom; envelope-from="f@example.com"; helo=example.com; client-ip=10.0.0.1 - - #TDspf Policy action=PREPEND Received-SPF: neutral (example.com: Domain does not state whether sender is authorized to use 'f@example.com' in 'mfrom' identity (mechanism '?all' matched)) receiver=sample.net identity=mfrom; envelope-from="f@example.com"; helo="[10.0.0.1]"; client-ip=192.168.0.1 - - #TDspf Policy action=PREPEND Received-SPF: none (example.com: No applicable sender policy available) receiver=sample.net; identity=helo; helo=example.com; client-ip=192.168.0.1 - #TDspf Policy action=PREPEND Received-SPF: none (example.com: No applicable sender policy available) receiver=mx1.example - - #print "LINE: $iehc\n"; - if ($iehc) { - %params = $iehc =~ /([-\w]+)=([^;]+)/g; - - if (exists $params{'identity'}) { - $params{'identity'} =~ s/identity=//; - if ($params{'identity'} eq 'mfrom' or $params{'identity'} eq 'mailfrom') { - $params{'identity'} = 'mail from'; - } - $params{'identity'} = uc $params{'identity'}; - } - $params{'envelope-from'} =~ s/"//g if exists $params{'envelope-from'}; - #($helo = $params{'helo'}) =~ s/"//g if exists $params{'helo'}; - $ip = $params{'client-ip'} if exists $params{'client-ip'}; - } - - $message =~ s/^([^:]+): // and $subject = $1; - - if ($message =~ /^No applicable sender policy available$/) { - $message = 'No sender policy'; - } - elsif ($message =~ s/^(Junk encountered in mechanism) '(.*?)'/$1/) { - #TDspf Policy action=PREPEND Received-SPF: permerror (example.com: Junk encountered in mechanism 'a:10.0.0.1') receiver=example.net; identity=mfrom; envelope-from="ef@example.com"; helo=h; client-ip=10.0.0.2 - $ip = formathost ($ip, 'mech: ' . $2); - } - elsif ($message =~ s/^(Included domain) '(.*?)' (has no .*)$/$1 $3/) { - #TDspf Policy action=PREPEND Received-SPF: permerror (example.com: Included domain 's.example.net' has no applicable sender policy) receiver=x.sample.com; identity=mfrom; envelope-from="ef@example.com"; helo=example.net; client-ip=10.0.0.2 - $subject .= " (included: $2)"; - } - elsif ($message =~ /^Domain does not state whether sender is authorized to use '.*?' in '\S+' identity \(mechanism '(.+?)' matched\)$/) { - # Domain does not state whether sender is authorized to use 'returns@example.com' in 'mfrom' identity (mechanism '?all' matched)) - ($mechanism,$message) = ($1,'Domain does not state if sender authorized to use'); - } - elsif ($message =~ /^(\S+) is (not )?authorized( by default)? to use '.*?' in '\S+' identity(?:, however domain is not currently prepared for false failures)? \(mechanism '(.+?)' matched\)$/) { - # Sender is not authorized by default to use 'from@example.com' in 'mfrom' identity, however domain is not currently prepared for false failures (mechanism '~all' matched)) - # 192.168.1.10 is authorized by default to use 'from@example.com' in 'mfrom' identity (mechanism 'all' matched)) - $message = join (' ', - $1 eq 'Sender' ? 'Sender' : 'IP', # canonicalize IP address - $2 ? 'not authorized' : 'authorized', - $3 ? 'by default to use' : 'to use', - ); - $mechanism = $4; - } - elsif ($message =~ /^Maximum DNS-interactive terms limit \S+ exceeded$/) { - $message = 'Maximum DNS-interactive terms limit exceeded'; - } - elsif ($message =~ /^Invalid IPv4 prefix length encountered in (.*)$/) { - $subject .= " (invalid: $1)"; - $message = 'Invalid IPv4 prefix length encountered'; - } - - #print "Result: $result, Identity: $params{'identity'}, Mech: $mechanism, Subject: $subject, IP: $ip\n"; - $Totals{'policyspf'}++; - if ($Logreporters::TreeData::Collecting{'policyspf'}) { - $message = join (' ', $message, $params{'identity'}) if exists $params{'identity'}; - $Counts{'policyspf'}{'Policy Action'}{"SPF $result"}{$message}{'mech: ' .$mechanism}{$subject}{$ip}++ - } - return; - } - - inc_unmatched('postfix_policy_spf(2)'); - return; - } - -=pod - Mail::SPF::Query - libmail-spf-query-perl 1:1.999 - - XXX incomplete - - Some possible smtp_comment results: - pass "localhost is always allowed." - none "SPF", "domain of sender $query->{sender} does not designate mailers - unknown $explanation, "domain of sender $query->{sender} does not exist" - $query->{spf_error_explanation}, $query->is_looping - $query->{spf_error_explanation}, $directive_set->{hard_syntax_error} - $query->{spf_error_explanation}, "Missing SPF record at $query->{domain}" - error $query->{spf_error_explanation}, $query->{error} - - $result $explanation, $comment, $query->{directive_set}->{orig_txt} - - Possible header_comment results: - pass "$query->{spf_source} designates $ip as permitted sender" - fail "$query->{spf_source} does not designate $ip as permitted sender" - softfail "transitioning $query->{spf_source} does not designate $ip as permitted sender" - /^unknown / "encountered unrecognized mechanism during SPF processing of $query->{spf_source}" - unknown "error in processing during lookup of $query->{sender}" - neutral "$ip is neither permitted nor denied by domain of $query->{sender}" - error "encountered temporary error during SPF processing of $query->{spf_source}" - none "$query->{spf_source} does not designate permitted sender hosts" - "could not perform SPF query for $query->{spf_source}" ); -=cut - - #TDspf 39053DC: SPF none: smtp_comment=SPF: domain of sender user@example.com does not designate mailers, header_comment=sample.net: domain of user@example.com does not designate permitted sender hosts - #TDspf : SPF none: smtp_comment=SPF: domain of sender user@example.com does not designate mailers, header_comment=sample.net: domain of user@example.com does not designate permitted sender hosts - #TDspf : SPF pass: smtp_comment=Please see http://www.openspf.org/why.html?sender=user%40example.com&ip=10.0.0.1&receiver=sample.net: example.com MX mail.example.com A 10.0.0.1, header_comment=example.com: domain of user@example.com designates 10.0.0.1 as permitted sender - #TDspf : SPF fail: smtp_comment=Please see http://www.openspf.org/why.html?sender=user%40example.com&ip=10.0.0.1&receiver=sample.net, header_comment=sample.net: domain of user@example.com does not designate 10.0.0.1 as permitted sender - #TDspf : : SPF none: smtp_comment=SPF: domain of sender does not designate mailers, header_comment=mx1.example.com: domain of does not designate permitted sender hosts - - if (my ($result, $reply) = ($line =~ /^(SPF [^:]+): (.*)$/)) { - - #print "result: $result\n\treply: $reply\n\tORIG: \"$Logreporters::Reports::origline\"\n"; - - if ($reply =~ /^(?:smtp_comment=)(.*)$/) { - $reply = $1; - - # SPF none - if ($reply =~ /^SPF: domain of sender (?:(?:[^@]+@)?(\S+) )?does not designate mailers/) { - $domain = $1 ? $1 : '*unknown'; - #print "result: $result: domain: $domain\n"; - } - elsif ($reply =~ /^Please see http:\/\/[^\/]+\/why\.html\?sender=(?:.+%40)?([^&]+)&ip=([^&]+)/) { - ($domain,$ip) = ($1,$2); - #print "result: $result: domain: $domain, IP: $ip\n"; - } - - # SPF unknown - elsif ($reply =~ /^SPF record error: ([^,]+), .*: error in processing during lookup of (?:[^@]+\@)?(\S+)/) { - ($message, $domain) = ($1, $2); - #print "result: $result: domain: $domain, Problem: $message\n"; - } - elsif ($reply =~ /^SPF record error: ([^,]+), .*: encountered unrecognized mechanism during SPF processing of domain (?:[^@]+\@)?(\S+)/) { - ($message, $domain) = ($1,$2); - #print "result: \"$result\": domain: $domain, Problem: $message\n"; - $result = "SPF permerror" if ($result =~ /SPF unknown mx-all/); - } - else { - inc_unmatched('postfix_policy_spf(3)'); - return; - } - } - else { - inc_unmatched('postfix_policy_spf(4)'); - return; - } - - $Totals{'policyspf'}++; - if ($message) { - $Counts{'policyspf'}{'Policy Action'}{$result}{$domain}{$ip}{$message}{$END_KEY}++ if ($Logreporters::TreeData::Collecting{'policyspf'}); - } - else { - $Counts{'policyspf'}{'Policy Action'}{$result}{$domain}{$ip}{$END_KEY}++ if ($Logreporters::TreeData::Collecting{'policyspf'}); - } - return; - } - - - inc_unmatched('postfix_policy_spf(5)'); -} - -1; - -#MODULE: ../Logreporters/Postfwd.pm -package Logreporters::Postfwd; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); - @EXPORT = qw(&postfix_postfwd); -} - -use subs @EXPORT; - -BEGIN { - import Logreporters::TreeData qw(%Totals %Counts $END_KEY); - import Logreporters::Utils; - import Logreporters::Reports qw(&inc_unmatched); -} - -# postfwd: http://postfwd.org/ -# -# -sub postfix_postfwd($) { - my $line = shift; - - return if ( - #TDpfw [STATS] Counters: 213000 seconds uptime, 39 rules - #TDpfw [LOGS info]: compare rbl: "example.com[10.1.0.7]" -> "localrbl.local" - #TDpfw [DNSBL] object 10.0.0.1 listed on rbl:list.dnswl.org (answer: 127.0.15.0, time: 0s) - $line =~ /^\[STATS\] / or - $line =~ /^\[DNSBL\] / or - $line =~ /^\[LOGS info\]/ or - $line =~ /^Process Backgrounded/ or - $line =~ /^Setting [ug]id to/ or - $line =~ /^Binding to TCP port/ or - $line =~ /^terminating\.\.\./ or - $line =~ /^Setting status interval to \S+ seconds/ or - $line =~ /^postfwd .+ ready for input$/ or - $line =~ /postfwd .+ (?:starting|terminated)/ - ); - - my ($type,$rule,$id,$action,$host,$hostip,$recipient); - - if ($line =~ /^\[(RULES|CACHE)\] rule=(\d+), id=([^,]+), client=([^[]+)\[([^]]+)\], sender=.*?, recipient=<(.*?)>,.*? action=(.*)$/) { - #TDpfw [RULES] rule=0, id=OK_DNSWL, client=example.com[10.0.0.1], sender=<f@example.com>, recipient=<to@sample.net>, helo=<example.com>, proto=ESMTP, state=RCPT, delay=0s, hits=OK_DNSWL, action=DUNNO - #TDpfw [CACHE] rule=14, id=GREY_NODNS, client=unknown[192.168.0.1], sender=<f@example.net>, recipient=<to@sample.com>, helo=<example.com>, proto=ESMTP, state=RCPT, delay=0s, hits=SET_NODNS;EVAL_DNSBLS;EVAL_RHSBLS;GREY_NODNS, action=greylist - ($type,$rule,$id,$host,$hostip,$recipient,$action) = ($1,$2,$3,$4,$5,$6,$7); - $recipient = '*unknown' if (not defined $recipient); - $Counts{'postfwd'}{"Rule $rule"}{$id}{$action}{$type}{$recipient}{formathost($hostip,$host)}++ if ($Logreporters::TreeData::Collecting{'postfwd'}); - } - elsif (($line =~ /Can't connect to TCP port/) or - ($line =~ /Pid_file already exists for running process/) - ) - { - $line =~ s/^[-\d\/:]+ //; # strip leading date/time stamps 2009/07/18-20:09:49 - $Totals{'warningsother'}++; return unless ($Logreporters::TreeData::Collecting{'warningsother'}); - $Counts{'warningsother'}{"$Logreporters::service_name: $line"}++; - return; - } - - # ignoring [DNSBL] lines - #elsif ($line =~ /^\[DNSBL\] object (\S+) listed on (\S+) \(answer: ([^,]+), .*\)$/) { - # #TDpfw [DNSBL] object 10.0.0.60 listed on rbl:list.dnswl.org (answer: 127.0.15.0, time: 0s) - # ($type,$rbl) = split (/:/, $2); - # $Counts{'postfwd'}{"DNSBL: $type"}{$rbl}{$1}{$3}{''}++ if ($Logreporters::TreeData::Collecting{'postfwd'}); - #} - else { - inc_unmatched('postfwd'); - return; - } - - $Totals{'postfwd'}++; -} - -1; - -#MODULE: ../Logreporters/Postgrey.pm -package Logreporters::Postgrey; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -my (%pgDelays,%pgDelayso); - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); - @EXPORT = qw(&postfix_postgrey &print_postgrey_reports); -} - -use subs @EXPORT; - -BEGIN { - import Logreporters::TreeData qw(%Totals %Counts $END_KEY); - import Logreporters::Utils; - import Logreporters::Config qw(%Opts); - import Logreporters::Reports qw(&inc_unmatched &print_percentiles_report2); -} - -# postgrey: http://postgrey.schweikert.ch/ -# -# Triplet: (client IP, envelope sender, envelope recipient address) -# -sub postfix_postgrey($) { - my $line = shift; - - return if ( - #TDpg cleaning up old logs... - #TDpg cleaning up old entries... - #TDpg cleaning clients database finished. before: 207, after: 207 - #TDpg cleaning main database finished. before: 3800, after: 2539 - #TDpg delayed 603 seconds: client=10.0.example.com, from=anyone@sample.net, to=joe@example.com - - #TDpg Setting uid to "504" - #TDpg Setting gid to "1002 1002" - #TDpg Process Backgrounded - #TDpg 2008/03/08-15:54:49 postgrey (type Net::Server::Multiplex) starting! pid(21961) - #TDpg Binding to TCP port 10023 on host 127.0.0.1 - #TDpg 2007/01/25-14:58:24 Server closing! - #TDpg Couldn't unlink "/var/run/postgrey.pid" [Permission denied] - #TDpg ignoring garbage: <help> - #TDpg unrecognized request type: '' - #TDpg rm /var/spool/postfix/postgrey/log.0000000002 - #TDpg 2007/01/25-14:48:00 Pid_file already exists for running process (4775)... aborting at line 232 in file /usr/lib/perl5/vendor_perl/5.8.7/Net/Server.pm - - - $line =~ /^cleaning / or - $line =~ /^delayed / or - $line =~ /^cleaning / or - $line =~ /^Setting [ug]id/ or - $line =~ /^Process Backgrounded/ or - $line =~ /^Binding to / or - $line =~ /^Couldn't unlink / or - $line =~ /^ignoring garbage: / or - $line =~ /^unrecognized request type/ or - $line =~ /^rm / or - # unanchored last - $line =~ /Pid_file already exists/ or - $line =~ /postgrey .* starting!/ or - $line =~ /Server closing!/ - ); - - my ($action,$reason,$delay,$host,$ip,$sender,$recip); - - if ($line =~ /^(?:$Logreporters::re_QID: )?action=(.*?), reason=(.*?)(?:, delay=(\d+))?, client_name=(.*?), client_address=(.*?)(?:, sender=(.*?))?(?:, +recipient=(.*))?$/o) { - #TDpg action=greylist, reason=new, client_name=example.com, client_address=10.0.0.1, sender=from@example.com, recipient=to@sample.net - #TDpgQ action=greylist, reason=new, client_name=example.com, client_address=10.0.0.1, sender=from@example.com - #TDpgQ action=pass, reason=triplet found, client_name=example.com, client_address=10.0.0.1, sender=from@example.com, recipient=to@sample.net - #TDpg action=pass, reason=triplet found, client_name=example.com, client_address=10.0.0.1, sender=from@example.com, recipient=to@sample.net - #TDpg action=pass, reason=triplet found, client_name=example.com, client_address=10.0.0.1, recipient=to@sample.net - #TDpg action=pass, reason=triplet found, delay=99, client_name=example.com, client_address=10.0.0.1, recipient=to@sample.net - ($action,$reason,$delay,$host,$ip,$sender,$recip) = ($1,$2,$3,$4,$5,$6,$7); - $reason =~ s/^(early-retry) \(.* missing\)$/$1/; - $recip = '*unknown' if (not defined $recip); - $sender = '' if (not defined $sender); - - $Totals{'postgrey'}++; - if ($Logreporters::TreeData::Collecting{'postgrey'}) { - $Counts{'postgrey'}{"\u$action"}{"\u$reason"}{formathost($ip,$host)}{$recip}{$sender}++; - - if (defined $delay and $Logreporters::TreeData::Collecting{'postgrey_delays'}) { - $pgDelays{'1: Total'}{$delay}++; - - push @{$pgDelayso{'Postgrey'}}, $delay - } - } - } - elsif ($line =~ /^whitelisted: (.*?)(?:\[([^]]+)\])?$/) { - #TDpg: whitelisted: example.com[10.0.0.1] - $Totals{'postgrey'}++; - if ($Logreporters::TreeData::Collecting{'postgrey'}) { - $Counts{'postgrey'}{'Whitelisted'}{defined $2 ? formathost($2,$1) : $1}{$END_KEY}++; - } - } - elsif ($line =~ /^tarpit whitelisted: (.*?)(?:\[([^]]+)\])?$/) { - #TDpg: tarpit whitelisted: example.com[10.0.0.1] - $Totals{'postgrey'}++; - if ($Logreporters::TreeData::Collecting{'postgrey'}) { - $Counts{'postgrey'}{'Tarpit whitelisted'}{defined $2 ? formathost($2,$1) : $1}{$END_KEY}++; - } - } - else { - inc_unmatched('postgrey'); - } - - return; -} - -sub print_postgrey_reports() { - #print STDERR "pgDelays memory usage: ", commify(Devel::Size::total_size(\%pgDelays)), "\n"; - - if ($Opts{'postgrey_delays'}) { - my @table; - for (sort keys %pgDelays) { - # anon array ref: label, array ref of $Delay{key} - push @table, [ substr($_,3), $pgDelays{$_} ]; - } - if (@table) { - print_percentiles_report2(\@table, "Postgrey Delays Percentiles", $Opts{'postgrey_delays_percentiles'}); - } - } -} - -1; - -#MODULE: ../Logreporters/PolicydWeight.pm -package Logreporters::PolicydWeight; - -use 5.008; -use strict; -use re 'taint'; -use warnings; - -BEGIN { - use Exporter (); - use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); - $VERSION = '1.000'; - @ISA = qw(Exporter); - @EXPORT = qw(&postfix_policydweight); -} - -use subs @EXPORT; - -BEGIN { - import Logreporters::Reports qw(&inc_unmatched); - import Logreporters::TreeData qw(%Totals %Counts); - import Logreporters::Utils; -} - -# Handle postfix/policydweight entries -# -sub postfix_policydweight($) { - my $line = shift; - my ($r1, $code, $reason, $reason2); - - if ( - $line =~ /^weighted check/ or - $line =~ /^policyd-weight .* started and daemonized/ or - $line =~ /^(cache|child|master): / or - $line =~ /^cache (?:spawned|killed)/ or - $line =~ /^child \d+ exited/ or - $line =~ /^Daemon terminated/ or - $line =~ /^Daemon terminated/ - ) - { - #print "$OrigLine\n"; - return; - } - - if ($line =~ s/^decided action=//) { - $line =~ s/; delay: \d+s$//; # ignore, eg.: "delay: 3s" - #print "....\n\tLINE: $line\n\tORIG: '$Logreporters::Reports::origline'\n"; - if (($code,$r1) = ($line =~ /^(\d+)\s+(.*)$/ )) { - my @problems = (); - for (split /; */, $r1) { - - if (/^Mail appeared to be SPAM or forged\. Ask your Mail\/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs/ ) { - push @problems, 'spam/forged: bad DNS/hit DNSRBLs'; - } - elsif (/^Your MTA is listed in too many DNSBLs/) { - push @problems, 'too many DNSBLs'; - } - elsif (/^temporarily blocked because of previous errors - retrying too fast\. penalty: \d+ seconds x \d+ retries\./) { - push @problems, 'temp blocked: retrying too fast'; - } - elsif (/^Please use DynDNS/) { - push @problems, 'use DynDNS'; - } - elsif (/^please relay via your ISP \([^)]+\)/) { - push @problems, 'use ISP\'s relay'; - } - elsif (/^in (.*)/) { - push @problems, $1; - } - elsif (m#^check http://rbls\.org/\?q=#) { - push @problems, 'see http://rbls.org'; - } - elsif (/^MTA helo: .* \(helo\/hostname mismatch\)/) { - push @problems, 'helo/hostname mismatch'; - } - elsif (/^No DNS entries for your MTA, HELO and Domain\. Contact YOUR administrator\s+/) { - push @problems, 'no DNS entries'; - } - else { - push @problems, $_; - } - } - - $reason = $code; $reason2 = join (', ', @problems); - } - elsif ($line =~ s/^DUNNO\s+//) { - #decided action=DUNNO multirecipient-mail - already accepted by previous query; delay: 0s - $reason = 'DUNNO'; $reason2 = $line; - } - elsif ($line =~ s/^check_greylist//) { - #decided action=check_greylist; delay: 16s - $reason = 'Check greylist'; $reason2 = $line; - } - elsif ($line =~ s/^PREPEND X-policyd-weight:\s+//) { - #decided action=PREPEND X-policyd-weight: using cached result; rate: -7.6; delay: 0s - if ($line =~ /(using cached result); rate:/) { - $reason = 'PREPEND X-policyd-weight: mail accepted'; $reason2 = "\u$1"; - } - else { - #decided action=PREPEND X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 P0F_LINUX=0 <client=10.0.0.1> <helo=example.com> <from=f@example.com> <to=t@sample.net>, rate: -7.6; delay: 2s - $reason = 'PREPEND X-policyd-weight: mail accepted'; $reason2 = 'Varies'; - } - } - else { - return; - } - } - elsif ($line =~ /^err/) { - # coerrce policyd-weight err's into general warnings - $Totals{'startuperror'}++; - $Counts{'startuperror'}{'Service: policyd-weight'}{$line}++ if ($Logreporters::TreeData::Collecting{'startuperror'}); - return; - } - else { - inc_unmatched('policydweight'); - return; - } - - $Totals{'policydweight'}++; - $Counts{'policydweight'}{$reason}{$reason2}++ if ($Logreporters::TreeData::Collecting{'policydweight'}); -} - -1; - - -package Logreporters; - -BEGIN { - import Logreporters::Utils; - import Logreporters::Config; - import Logreporters::TreeData qw(%Totals %Counts %Collecting printTree buildTree $END_KEY); - import Logreporters::RegEx; - import Logreporters::Reports; - import Logreporters::RFC3463; - import Logreporters::PolicySPF; - import Logreporters::Postfwd; - import Logreporters::Postgrey; - import Logreporters::PolicydWeight; -} -use 5.008; -use strict; -use warnings; -no warnings "uninitialized"; -use re 'taint'; - -use File::Basename; -our $progname = fileparse($0); - -my @supplemental_reports = qw(delays postgrey_delays); - -# Default values for various options. These are used -# to reset default values after an option has been -# disabled (via undef'ing its value). This allows -# a report to be disabled via config file or --nodetail, -# but reenabled via subsequent command line option -my %Defaults = ( - detail => 10, # report level detail - max_report_width => 100, # maximum line width for report output - line_style => undef, # lines > max_report_width, 0=truncate,1=wrap,2=full - syslog_name => 'postfix', # service name (postconf(5), syslog_name) - sect_vars => 0, # show section vars in detail report hdrs - unknown => 1, # show 'unknown' in address/hostname pairs - ipaddr_width => 15, # width for printing ip addresses - long_queue_ids => 0, # enable long queue ids (2.9+) - delays => 1, # show message delivery delays report - delays_percentiles => '0 25 50 75 90 95 98 100', # percentiles shown in delays report - reject_reply_patterns => '5.. 4.. warn', # reject reply grouping patterns - postgrey_delays => 1, # show postgrey delays report - postgrey_delays_percentiles => '0 25 50 75 90 95 98 100', # percentiles shown in postgrey delays report -); - -my $usage_str = <<"END_USAGE"; -Usage: $progname [ ARGUMENTS ] [logfile ...] - ARGUMENTS can be one or more of options listed below. Later options - override earlier ones. Any argument may be abbreviated to an unambiguous - length. Input is read from the named logfile(s), or STDIN. - - --debug AREAS provide debug output for AREAS - --help print usage information - --version print program version - - --config_file FILE, -f FILE use alternate configuration file FILE - --ignore_services PATTERN ignore postfix/PATTERN services - --syslog_name PATTERN only consider log lines that match - syslog service name PATTERN - - --detail LEVEL print LEVEL levels of detail - (default: 10) - --nodetail set all detail levels to 0 - --[no]summary display the summary section - - --ipaddr_width WIDTH use WIDTH chars for IP addresses in - address/hostname pairs - --line_style wrap|full|truncate disposition of lines > max_report_width - (default: truncate) - --full same as --line_style=full - --truncate same as --line_style=truncate - --wrap same as --line_style=wrap - --max_report_width WIDTH limit report width to WIDTH chars - (default: 100) - --limit L=V, -l L=V set level limiter L with value V - --[no]long_queue_ids use long queue ids - --[no]unknown show the 'unknown' hostname in - formatted address/hostnames pairs - --[no]sect_vars [do not] show config file var/cmd line - option names in section titles - - --recipient_delimiter C split delivery addresses using - recipient delimiter char C - --reject_reply_patterns "R1 [R2 ...]" set reject reply patterns used in - to group rejects to R1, [R2 ...], - where patterns are [45][.0-9][.0-9] - or "Warn" (default: 5.. 4.. Warn) - Supplimental reports - --[no]delays [do not] show msg delays percentiles report - --delays_percentiles "P1 [P2 ...]" set delays report percentiles to - P1 [P2 ...] (range: 0...100) - --[no]postgrey_delays [do not] show postgrey delays percentiles - report - --postgrey_delays_percentiles "P1 [P2 ...]" - set postgrey delays report percentiles to - P1 [P2 ...] (range: 0...100) -END_USAGE - -my @RejectPats; # pattern list used to match against reject replys -my @RejectKeys; # 1-to-1 with RejectPats, but with 'x' replacing '.' (for report output) -my (%DeferredByQid, %SizeByQid, %AcceptedByQid, %Delays); - -# local prototypes -sub usage; -sub init_getopts_table; -sub init_defaults; -sub build_sect_table; -sub postfix_bounce; -sub postfix_cleanup; -sub postfix_panic; -sub postfix_fatal; -sub postfix_error; -sub postfix_warning; -sub postfix_script; -sub postfix_postsuper; -sub process_delivery_attempt; -sub cleanhostreply; -sub strip_ftph; -sub get_reject_key; -sub expand_bare_reject_limiters; -sub create_ignore_list; -sub in_ignore_list; -sub header_body_checks; -sub milter_common; - -# lines that match any RE in this list will be ignored. -# see create_ignore_list(); -my @ignore_list = (); - -# The Sections table drives Summary and Detail reports. For each entry in the -# table, if there is data avaialable, a line will be output in the Summary report. -# Additionally, a sub-section will be output in the Detail report if both the -# global --detail, and the section's limiter variable, are sufficiently high (a -# non-existent section limiter variable is considered to be sufficiently high). -# -my @Sections = (); - -# List of reject variants. See also: "Add reject variants" below, and conf file(s). -my @RejectClasses = qw( - rejectrelay rejecthelo rejectdata rejectunknownuser rejectrecip rejectsender - rejectclient rejectunknownclient rejectunknownreverseclient rejectunverifiedclient - rejectrbl rejectheader rejectbody rejectcontent rejectsize rejectmilter rejectproxy - rejectinsufficientspace rejectconfigerror rejectverify rejectetrn rejectlookupfailure -); - -# Dispatch table of the list of supported policy services -# XXX have add-ins register into the dispatch table -my @policy_services = ( - [ qr/^postfwd/, \&Logreporters::Postfwd::postfix_postfwd ], - [ qr/^postgrey/, \&Logreporters::Postgrey::postfix_postgrey ], - [ qr/^policyd?-spf/, \&Logreporters::PolicySPF::postfix_policy_spf ], - [ qr/^policyd-?weight/, \&Logreporters::PolicydWeight::postfix_policydweight ], -); - -# Initialize main running mode and basic opts -init_run_mode($config_file); - -# Configure the Getopts options table -init_getopts_table(); - -# Place configuration file/environment variables onto command line -init_cmdline(); - -# Initialize default values -init_defaults(); - -# Process command line arguments, 0=no_permute,no_pass_through -get_options(0); - -# Build the Section table, after reject_reply_patterns is final -build_sect_table(); - -# Expand bare rejects before generic processing -expand_bare_reject_limiters(); - -# Run through the list of Limiters, setting the limiters in %Opts. -process_limiters(@Sections); - -# Set collection for any enabled supplemental sections -foreach (@supplemental_reports) { - $Logreporters::TreeData::Collecting{$_} = (($Opts{'detail'} >= 5) && $Opts{$_}) ? 1 : 0; -} - -if (! defined $Opts{'line_style'}) { - # default line style to full if detail >= 11, or truncate otherwise - $Opts{'line_style'} = - ($Opts{'detail'} > 10) ? $line_styles{'full'} : $line_styles{'truncate'}; -} - -# Set the QID RE to capture either pre-2.9 short style or 2.9+ long style. -$re_QID = $Opts{'long_queue_ids'} ? $re_QID_l : $re_QID_s; - -# Create the list of REs used to match against log lines -create_ignore_list(); - -# Notes: -# -# - IN REs, always use /o flag or qr// at end of RE when RE uses interpolated vars -# - In REs, email addresses may be empty "<>" - capture using *, not + ( eg. from=<.*?> ) -# - See additional notes below, search for "Note:". -# - XXX indicates change, fix or thought required - - -# Main processing loop -# -LINE: while ( <> ) { - chomp; - s/\s+$//; - next unless length $_; - - $Logreporters::Reports::origline = $_; - - # Linux: Jul 1 20:08:06 mailhost postfix/smtpd[4379]: connect from unknown[10.0.0.1] - # FreeBSD: Jul 1 20:08:06 <mail.info> mailhost postfix/smtpd[4379]: connect from unknown[10.0.0.1] - # Aug 17 15:16:12 mailhost postfix/cleanup[14194]: [ID 197553 mail.info] EC2B339E5: message-id=<2616.EC2B339E5@example.com> - # Dec 25 05:20:28 mailhost policyd-spf[14194]: [ID 27553 mail.info] ... policyd-spf stuff ... - - next unless /^[A-Z][a-z]{2} [ \d]\d \d{2}:\d{2}:\d{2} (?:<[^>]+> )?(\S+) ($Opts{'syslog_name'}(?:\/([^:[]+))?)(?:\[\d+\])?: (?:\[ID \d+ \w+\.\w+\] )?(.*)$/o; - - our $service_name = $3; - my ($mailhost,$server_name,$p1) = ($1,$2,$4); - #print "mailhost: $mailhost, servername: $server_name, servicename: $service_name, p1: $p1\n"; - - $service_name = $server_name unless $service_name; - #print "service_name: $service_name\n"; - - # ignored postfix services... - next if $service_name eq 'postlog'; - next if $service_name =~ /^$Opts{'ignore_services'}$/o; - - # common log entries up front - if ($p1 =~ s/^connect from //) { - #TD25 connect from sample.net[10.0.0.1] - #TD connect from mail.example.com[2001:dead:beef::1] - #TD connect from localhost.localdomain[127.0.0.1] - #TD connect from unknown[unknown] - $Totals{'connectioninbound'}++; - next unless ($Collecting{'connectioninbound'}); - - my $host = $p1; my $hostip; - if (($host,$hostip) = ($host =~ /^([^[]+)\[([^]]+)\]/)) { - $host = formathost($hostip,$host); - } - $Counts{'connectioninbound'}{$host}++; - next; - } - - if ($p1 =~ /^disconnect from /) { - #TD25 disconnect from sample.net[10.0.0.1] - #TD disconnect from mail.example.com[2001:dead:beef::1] - $Totals{'disconnection'}++; - next; - } - - if ($p1 =~ s/^connect to //) { - next if ($p1 =~ /^subsystem /); - $Totals{'connecttofailure'}++; - next unless ($Collecting{'connecttofailure'}); - - my ($host,$hostip,$reason,$port) = ($p1 =~ /^([^[]+)\[([^]]+)\](?::\d+)?: (.*?)(?:\s+\(port (\d+)\))?$/); - # all "connect to" messages indicate a problem with the connection - #TDs connect to example.org[10.0.0.1]: Connection refused (port 25) - #TDs connect to mail.sample.com[10.0.0.1]: No route to host (port 25) - #TDs connect to sample.net[192.168.0.1]: read timeout (port 25) - #TDs connect to mail.example.com[10.0.0.1]: server dropped connection without sending the initial SMTP greeting (port 25) - #TDs connect to mail.example.com[192.168.0.1]: server dropped connection without sending the initial SMTP greeting (port 25) - #TDs connect to ipv6-1.example.com[2001:dead:beef::1]: Connection refused (port 25) - #TDs connect to ipv6-2.example.com[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]: Connection refused (port 25) - #TDs connect to ipv6-3.example.com[1080:0:0:0:8:800:200C:4171]: Connection refused (port 25) - #TDs connect to ipv6-4.example.com[3ffe:2a00:100:7031::1]: Connection refused (port 25) - #TDs connect to ipv6-5.example.com[1080::8:800:200C:417A]: Connection refused (port 25) - #TDs connect to ipv6-6.example.com[::192.9.5.5]: Connection refused (port 25) - #TDs connect to ipv6-7.example.com[::FFFF:129.144.52.38]: Connection refused (port 25) - #TDs connect to ipv6-8.example.com[2010:836B:4179::836B:4179]: Connection refused (port 25) - #TDs connect to mail.example.com[10.0.0.1]: server refused to talk to me: 452 try later (port 25) - - $host = join(' :', $host, $port) if ($port and $port ne '25'); - # Note: See ConnectToFailure below - if ($reason =~ /^server (refused to talk to me): (.*)$/) { - $Counts{'connecttofailure'}{ucfirst($1)}{formathost($hostip,$host)}{$2}++; - } else { - $Counts{'connecttofailure'}{ucfirst($reason)}{formathost($hostip,$host)}{''}++; - } - next; - } - -=pod -real 3m43.997s -user 3m39.038s -sys 0m3.005s -=pod - # Handle before panic, fatal, warning, so that service-specific code gets first crack - # XXX replace w/dispatch table for add-ins, so user's can add their own... - if ($service_name eq 'postfwd') { postfix_postfwd($p1); next; } - if ($service_name eq 'postgrey') { postfix_postgrey($p1); next; } - if ($service_name =~ /^policyd?-spf/) { postfix_policy_spf($p1); next; } # postfix/policy-spf - if ($service_name =~ /^policyd-?weight/) { postfix_policydweight($p1); next; } # postfix/policydweight - -=cut - # Handle policy service handlers before panic, fatal, warning, etc. - # messages so that service-specific code gets first crack. - # 5:25 - foreach (@policy_services) { - if ($service_name =~ $_->[0]) { - #print "Calling policy service helper: $service_name:('$p1')\n"; - &{$_->[1]}($p1); - next LINE; - } - }; -#=cut - - # ^warning: ... - # ^fatal: ... - # ^panic: ... - # ^error: ... - if ($p1 =~ /^warning: +(.*)$/) { postfix_warning($1); next; } - if ($p1 =~ /^fatal: +(.*)$/) { postfix_fatal($1); next; } - if ($p1 =~ /^panic: +(.*)$/) { postfix_panic($1); next; } - if ($p1 =~ /^error: +(.*)$/) { postfix_error($1); next; } - - # output by all services that use table lookups - process before specific messages - if ($p1 =~ /(?:lookup )?table (?:[^ ]+ )?has changed -- (?:restarting|exiting)$/) { - #TD table hash:/var/mailman/data/virtual-mailman(0,lock|fold_fix) has changed -- restarting - #TD table hash:/etc/postfix/helo_checks has changed -- restarting - $Totals{'tablechanged'}++; - next; - } - - # postfix/postscreen and postfix/verify services - if ($service_name eq 'postscreen' - or $service_name eq 'verify') { postfix_postscreen($p1); next; } # postfix/postscreen, postfix/verify - if ($service_name eq 'dnsblog') { postfix_dnsblog($p1); next; } # postfix/dnsblog - if ($service_name =~ /^cleanup/) { postfix_cleanup($p1); next; } # postfix/cleanup* - if ($service_name =~ /^bounce/) { postfix_bounce($p1); next; } # postfix/bounce* - if ($service_name eq 'postfix-script') { postfix_script($p1); next; } # postfix/postfix-script - if ($service_name eq 'postsuper') { postfix_postsuper($p1); next; } # postfix/postsuper - - # ignore tlsproxy for now - if ($service_name eq 'tlsproxy') { next; } # postfix/tlsproxy - - my ($helo, $relay, $from, $origto, $to, $domain, $status, - $type, $reason, $reason2, $filter, $site, $cmd, $qid, - $rej_type, $reject_name, $host, $hostip, $dsn, $reply, $fmthost, $bytes); - - $rej_type = undef; - - # ^$re_QID: ... - if ($p1 =~ s/^($re_QID): //o) { - $qid = $1; - - next if ($p1 =~ /^host \S*\[\S*\] said: 4\d\d/); # deferrals, picked up in "status=deferred" - - if ($p1 =~ /^removed\s*$/ ) { - # Note: See REMOVED elsewhere - # 52CBDC2E0F: removed - delete $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'removedfromqueue'}++; - next; - } - - # coerce into general warning - if (($p1 =~ /^Cannot start TLS: handshake failure/) or - ($p1 =~ /^non-E?SMTP response from/)) { - postfix_warning($p1); - next; - } - - if ($p1 eq 'status=deferred (bounce failed)') { - #TDqQ status=deferred (bounce failed) - $Totals{'bouncefailed'}++; - next; - } - - # this test must preceed access checks below - #TDsQ replace: header From: "Postmaster" <postmaster@webmail.example.com>: From: "Postmaster" <postmaster@webmail.example.org> - if ($service_name eq 'smtp' and header_body_checks($p1)) { - #print "main: header_body_checks\n"; - next; - } - - # Postfix access actions - # REJECT optional text... - # DISCARD optional text... - # HOLD optional text... - # WARN optional text... - # FILTER transport:destination - # REDIRECT user@domain - # BCC user@domain (2.6 experimental branch) - # The following actions are indistinguishable in the logs - # 4NN text - # 5NN text - # DEFER_IF_REJECT optional text... - # DEFER_IF_PERMIT optional text... - # UCE restriction... - # The following actions are not logged - # PREPEND headername: headervalue - # DUNNO - # - # Reject actions based on remote client information: - # - one of host name, network address, envelope sender - # or - # - recipient address - - # Template of access controls. Rejects look like the first line, other access actions the second. - # ftph is envelope from, envelope to, proto and helo. - # QID: ACTION STAGE from host[hostip]: DSN trigger: explanation; ftph - # QID: ACTION STAGE from host[hostip]: trigger: explanation; ftph - - # $re_QID: reject: RCPT|MAIL|CONNECT|HELO|DATA from ... - # $re_QID: reject_warning: RCPT|MAIL|CONNECT|HELO|DATA from ... - if ($p1 =~ /^(reject(?:_warning)?|discard|filter|hold|redirect|warn|bcc|replace): /) { - my $action = $1; - $p1 = substr($p1, length($action) + 2); - - #print "action: \"$action\", p1: \"$p1\"\n"; - if ($p1 !~ /^(RCPT|MAIL|CONNECT|HELO|EHLO|DATA|VRFY|ETRN|END-OF-MESSAGE) from ([^[]+)\[([^]]+)\](?::\d+)?: (.*)$/) { - inc_unmatched('unexpected access'); - next; - } - my ($stage,$host,$hostip,$p1) = ($1,$2,$3,$4); #print "stage: \"$stage\", host: \"$host\", hostip: \"$hostip\", p1: \"$p1\"\n"; - my ($efrom,$eto,$proto,$helo) = strip_ftph($p1); #print "efrom: \"$efrom\", eto: \"$eto\", proto: \"$proto\", helo: \"$helo\"\n"; - #print "p1 now: \"$p1\"\n"; - -# QID: ACTION STAGE from host[hostip]: DSN trigger: explanation; ftph -#TDsdN reject_warning: VRFY from host[10.0.0.1]: 450 4.1.2 <<1F4@bs>>: Recipient address rejected: Domain not found; to=<<1F4@bs>> proto=SMTP helo=<friend> -#TDsdN reject: VRFY from host[10.0.0.1]: 550 5.1.1 <:>: Recipient address rejected: User unknown in local recipient table; to=<:> proto=SMTP helo=<10.0.0.1> -#TDsdN reject: VRFY from host[10.0.0.1]: 450 4.1.8 <to@example.com>: Sender address rejected: Domain not found; from=<f@sample.com> to=<eto@example.com> proto=SMTP -#TDsdN reject: VRFY from host[10.0.0.1]: 554 5.7.1 Service unavailable; Client host [10.0.0.1] blocked using zen.spamhaus.org; http://www.spamhaus.org/query/bl?ip=10.0.0.1; to=<u> proto=SMTP -#TDsdN reject: RCPT from host[10.0.0.1]: 450 4.1.2 <to@example.com>: Recipient address rejected: User unknown in local recipient table; from=<> to=<eto@example.com> proto=SMTP helo=<sample.net> -#TDsdN reject: RCPT from host[10.0.0.1]: 550 <to@example.com>: Recipient address rejected: User unknown in local recipient table; from=<> to=<eto@example.com> proto=SMTP helo=<sample.net> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 550 <to@example.com>: Recipient address rejected: User unknown in local recipient table; from=<> to=<eto@example.com> proto=SMTP helo=<sample.net> -#TDsdN reject: RCPT from host[10.0.0.1]: 550 5.1.1 <to@example.com>: Recipient address rejected: User unknown in virtual address table; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<localhost> -#TDsdN reject: RCPT from host[10.0.0.1]: 450 4.1.1 <to@sample.net>: Recipient address rejected: User unknown in virtual mailbox table; from=<f@sample.net> to=<eto@sample.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 550 5.5.0 <to@example.com>: Recipient address rejected: User unknown; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<[10.0.0.1]> -#TDsdN reject: RCPT from host[10.0.0.1]: 450 <to@example.net>: Recipient address rejected: Greylisted; from=<f@sample.net> to=<eto@example.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 454 4.7.1 <to@sample.net>: Recipient address rejected: Access denied; from=<f@sample.com> to=<eto@sample.net> proto=SMTP helo=<example.com> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 454 4.7.1 <to@sample.net>: Recipient address rejected: Access denied; from=<f@sample.net> to=<eto@sample.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 450 4.1.2 <to@example.com>: Recipient address rejected: Domain not found; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<sample.net> -#TDsdN reject: RCPT from host[10.0.0.1]: 554 <to@example.net>: Recipient address rejected: Please see http://www.openspf.org/why.html?sender=from%40example.net&ip=10.0.0.1&receiver=example.net; from=<from@example.net> to=<to@example.net> proto=ESMTP helo=<to@example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 550 <to@example.net>: Recipient address rejected: undeliverable address: host example.net[192.168.0.1] said: 550 <unknown@example.net>: User unknown in virtual alias table (in reply to RCPT TO command); from=<from@example.com> to=<unknown@example.net> proto=SMTP helo=<mail.example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 554 <to@example.com>: Recipient address rejected: Please see http://spf.pobox.com/why.html?sender=user%40example.com&ip=10.0.0.1&receiver=mail; from=<user@example.com> to=<to@sample.net> proto=ESMTP helo=<10.0.0.1> -#TDsdN reject: RCPT from host[10.0.0.1]: 554 <to@sample.net>: Relay access denied; from=<f@example.com> to=<eto@sample.net> proto=SMTP helo=<example.com> -#TDsdN reject_warning: HELO from host[10.0.0.1]: 554 <to@sample.net>: Relay access denied; proto=SMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 450 4.1.8 <f@sample.net>: Sender address rejected: Domain not found; from=<f@sample.com> to=<to@example.com> proto=ESMTP helo=<sample.net> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 450 4.1.8 <f@sample.net>: Sender address rejected: Domain not found; from=<f@sample.com> to=<to@example.com> proto=ESMTP helo=<sample.net> -#TDsdN reject: RCPT from host[10.0.0.1]: 550 <f@example.net>: Sender address rejected: undeliverable address: host example.net[10.0.0.1] said: 550 <f@example.net>: User unknown in virtual alias table (in reply to RCPT TO command); from=<f@example.net> to=<eto@example.net> proto=SMTP helo=<example.com> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 554 <host[10.0.0.1]>: Client host rejected: Access denied; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<friend> -#TDsdN reject: RCPT from host[10.0.0.1]: 554 <host[10.0.0.1]>: Client host rejected: Optional text; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<friend> -#TDsdN reject: CONNECT from host[10.0.0.1]: 503 5.5.0 <host[10.0.1]>: Client host rejected: Improper use of SMTP command pipelining; proto=SMTP - -#TDsdN reject_warning: RCPT from unk[10.0.0.1]: 450 Client host rejected: cannot find your hostname, [10.0.0.1]; from=<f@sample.com> to=<eto@sample.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from unk[10.0.0.1]: 450 Client host rejected: cannot find your hostname, [10.0.0.1]; from=<f@sample.com> to=<eto@sample.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from unk[10.0.0.1]: 450 Client host rejected: cannot find your hostname, [10.0.0.1]; proto=ESMTP -#TDsdN reject: RCPT from unk[10.0.0.1]: 550 5.7.1 Client host rejected: cannot find your reverse hostname, [10.0.0.1] -#TDsdN reject: CONNECT from unk[unknown]: 421 4.7.1 Client host rejected: cannot find your reverse hostname, [unknown]; proto=SMTP - -#TDsdN reject: RCPT from host[10.0.0.1]: 554 5.7.1 Service unavailable; Client host [10.0.0.1] blocked using sbl-xbl.spamhaus.org; http://www.spamhaus.org/query/bl?ip=10.0.0.1; from=<from@example.com> to=<to@sample.net> proto=ESMTP helo=<friend> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 554 5.7.1 Service unavailable; Client host [10.0.0.1] blocked using sbl-xbl.spamhaus.org; http://www.spamhaus.org/query/bl?ip=10.0.0.1; from=<from@example.com> to=<to@sample.net> proto=ESMTP helo=<friend> -#TDsdN reject: RCPT from host[10.0.0.1]: 554 Service denied; Client host [10.0.0.1] blocked using bl.spamcop.net; Blocked - see http://www.spamcop.net/bl.shtml?83.164.27.124; from=<bogus@example.com> to=<user@example.org> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 454 4.7.1 <localhost>: Helo command rejected: Access denied; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<localhost> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 454 4.7.1 <localhost>: Helo command rejected: Access denied; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<localhost> -#TDsdN reject: EHLO from host[10.0.0.1]: 504 5.5.2 <bogus>: Helo command rejected: need fully-qualified hostname; proto=SMTP helo=<bogus> -#TDsdQ reject: DATA from host[10.0.0.1]: 550 5.5.3 <DATA>: Data command rejected: Multi-recipient bounce; from=<> proto=ESMTP helo=<localhost> -#TDsdN reject: ETRN from host[10.0.0.1]: 554 5.7.1 <example.com>: Etrn command rejected: Access denied; proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 452 Insufficient system storage; from=<f@sample.com> to=<eto@sample.net> -#TDsdN reject_warning: RCPT from host[10.0.0.1]: 451 4.3.5 Server configuration error; from=<f@sample.com> to=<eto@sample.net> proto=ESMTP helo=<example.com> -#TDsdN reject: RCPT from host[10.0.0.1]: 450 Server configuration problem; from=<f@sample.net> to=<eto@sample.com> proto=ESMTP helo=<sample.net> -#TDsdN reject: MAIL from host[10.0.0.1]: 552 Message size exceeds fixed limit; proto=ESMTP helo=<localhost> -#TDsdN reject: RCPT from unk[10.0.0.1]: 554 5.7.1 <unk[10.0.0.1]>: Unverified Client host rejected: Access denied; from=<f@sample.net> to=<eto@sample.com> proto=SMTP helo=<sample.net> -#TDsdN reject: MAIL from host[10.0.0.1]: 451 4.3.0 <f@example.com>: Temporary lookup failure; from=<f@example.com> proto=ESMTP helo=<example.com> - - # reject, reject_warning - if ($action =~ /^reject/) { - my ($recip); - - if ($p1 !~ /^($re_DSN) (.*)$/o) { - inc_unmatched('reject1'); - next; - } - ($dsn,$p1) = ($1,$2); #print "dsn: $dsn, p1: \"$p1\"\n"; - $fmthost = formathost($hostip,$host); - - # reject_warning override temp or perm reject types - $rej_type = ($action eq 'reject_warning' ? 'warn' : get_reject_key($dsn)); - #print "REJECT stage: '$rej_type'\n"; - - if ($Collecting{'byiprejects'} and substr($rej_type,0,1) eq '5') { - $Counts{'byiprejects'}{$fmthost}++; - } - - if ($stage eq 'VRFY') { - if ($p1 =~ /^(?:<(\S*)>: )?(.*);$/) { - my ($trigger,$reason) = ($1,$2); - $Totals{$reject_name = "${rej_type}rejectverify" }++; next unless ($Collecting{$reject_name}); - - if ($reason =~ /^Service unavailable; Client host \[[^]]+\] (blocked using [^;]*);/) { - $reason = join (' ', 'Client host blocked using', $1); - $trigger = ''; - } - $Counts{$reject_name}{$reason}{$fmthost}{ucfirst($trigger)}++; - } else { - inc_unmatched('vrfyfrom'); - } - next; - } - - # XXX there may be several semicolon-separated messages - # Recipient address rejected: Unknown users and via check_recipient_access - if ($p1 =~ /^<(.*)>: Recipient address rejected: ([^;]*);/) { - ($recip,$reason) = ($1,$2); - - my ($localpart,$domainpart); - # Unknown users; local mailbox, alias, virtual, relay user, unspecified - if ($recip eq '') { ($localpart, $domainpart) = ('<>', '*unspecified'); } - else { - ($localpart, $domainpart) = split (/@/, lc $recip); - ($localpart, $domainpart) = ($recip, '*unspecified') if ($domainpart eq ''); - } - - if ($reason =~ s/^User unknown *//) { - $Totals{$reject_name = "${rej_type}rejectunknownuser" }++; next unless ($Collecting{$reject_name}); - - my ($table) = ($reason =~ /^in ((?:\w+ )+table)/); - $table = 'Address table unavailable' if ($table eq ''); # when show_user_unknown_table_name=no - $Counts{$reject_name}{ucfirst($table)}{$domainpart}{$localpart}{$fmthost}++; - } else { - # check_recipient_access - $Totals{$reject_name = "${rej_type}rejectrecip" }++; next unless ($Collecting{$reject_name}); - - if ($reason =~ m{^Please see http://[^/]+/why\.html}) { - $reason = 'SPF reject'; - } - elsif ($reason =~ /^undeliverable address: host ([^[]+)\[([^]]+)\](?::\d+)? said:/) { - $reason = 'undeliverable address: remote host rejected recipient'; - } - $Counts{$reject_name}{ucfirst($reason)}{$domainpart}{$localpart}{$fmthost}++; - } - - } elsif ($p1 =~ /^<(.*?)>.* Relay access denied/) { - $Totals{$reject_name = "${rej_type}rejectrelay" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$eto}++; - - } elsif ($p1 =~ /^<(.*)>: Sender address rejected: (.*);/) { - $Totals{$reject_name = "${rej_type}rejectsender" }++; next unless ($Collecting{$reject_name}); - ($from,$reason) = ($1,$2); - - if ($reason =~ /^undeliverable address: host ([^[]+)\[([^]]+)\](?::\d+)? said:/) { - $reason = 'undeliverable address: remote host rejected sender'; - } - $Counts{$reject_name}{ucfirst($reason)}{$fmthost}{$from ne '' ? $from : '<>'}++; - - } elsif ($p1 =~ /^(?:<.*>: )?Unverified Client host rejected: /) { - # check_reverse_client_hostname_access (postfix 2.6+) - $Totals{$reject_name = "${rej_type}rejectunverifiedclient" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$helo}{$eto}{$efrom}++; - - } elsif ($p1 =~ s/^(?:<.*>: )?Client host rejected: //) { - # reject_unknown_client - # client IP->name mapping fails - # name->IP mapping fails - # name->IP mapping =! client IP - if ($p1 =~ /^cannot find your hostname/) { - $Totals{$reject_name = "${rej_type}rejectunknownclient" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$helo}{$eto}{$efrom}++; - } - # reject_unknown_reverse_client_hostname (no PTR record for client's IP) - elsif ($p1 =~ /^cannot find your reverse hostname/) { - $Totals{$reject_name = "${rej_type}rejectunknownreverseclient" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$hostip}++ - } - else { - $Totals{$reject_name = "${rej_type}rejectclient" }++; next unless ($Collecting{$reject_name}); - $p1 =~ s/;$//; - $Counts{$reject_name}{ucfirst($p1)}{$fmthost}{$eto}{$efrom}++; - } - } elsif ($p1 =~ /^Service (?:temporarily )?(?:unavailable|denied)[^;]*; (?:(?:Unverified )?Client host |Sender address |Helo command )?\[[^ ]*\] blocked using ([^;]+);/) { - # Note: similar code below: search RejectRBL - - # postfix 2.1 - #TDsdN reject: RCPT from example.com[10.0.0.5]: 554 Service unavailable; Client host [10.0.0.5] blocked using bl.spamcop.net; Blocked - see http://www.spamcop.net/bl.shtml?10.0.0.5; from=<from@example.com> to=<to@example.net> proto=ESMTP helo=<example.com> - # postfix 2.3+ - #TDsdN reject: RCPT from example.com[10.0.0.6]: 554 5.7.1 Service unavailable; Client host [10.0.0.6] blocked using bl.spamcop.net; Blocked - see http://www.spamcop.net/bl.shtml?10.0.0.6; from=<from@example.com> to=<to@example.net> proto=SMTP helo=<example.com> - #TDsdN reject: RCPT from example.com[10.0.0.1]: 550 5.7.1 Service unavailable; Client host [10.0.0.1] blocked using Trend Micro RBL+. Please see http://www.mail-abuse.com/cgi-bin/lookup?ip_address=10.0.0.1; Mail from 10.0.0.1 blocked using Trend Micro Email Reputation database. Please see <http://www.mail-abuse.com/cgi-bin/lookup?10.0.0.1>; from=<from@example.com> to=<to@example.net> proto=SMTP helo=<10.0.0.1> - - $Totals{$reject_name = "${rej_type}rejectrbl" }++; next unless ($Collecting{$reject_name}); - ($site,$reason) = ($1 =~ /^(.+?)(?:$|(?:[.,] )(.*))/); - $reason =~ s/^reason: // if ($reason); - $Counts{$reject_name}{$site}{$fmthost}{$reason ? $reason : ''}++; - - } elsif ($p1 =~ /^<.*>: Helo command rejected: (.*);$/) { - $Totals{$reject_name = "${rej_type}rejecthelo" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{ucfirst($1)}{$fmthost}{$helo}++; - - } elsif ($p1 =~ /^<.*>: Etrn command rejected: (.*);$/) { - $Totals{$reject_name = "${rej_type}rejectetrn" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{ucfirst($1)}{$fmthost}{$helo}++; - - } elsif ($p1 =~ /^<.*>: Data command rejected: (.*);$/) { - $Totals{$reject_name = "${rej_type}rejectdata" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$1}{$fmthost}{$helo}++; - - } elsif ($p1 =~ /^Insufficient system storage;/) { - $Totals{'warninsufficientspace'}++; # force display in Warnings section also - $Totals{$reject_name = "${rej_type}rejectinsufficientspace" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$eto}{$efrom}++; - - } elsif ($p1 =~ /^Server configuration (?:error|problem);/) { - $Totals{'warnconfigerror'}++; # force display in Warnings section also - $Totals{$reject_name = "${rej_type}rejectconfigerror" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$eto}{$efrom}++; - - } elsif ($p1 =~ /^Message size exceeds fixed limit;$/) { - # Postfix responds with this message after a MAIL FROM:<...> SIZE=nnn command, where postfix consider's nnn excessive - # Note: similar code below: search RejectSize - # Note: reject_warning does not seem to occur - $Totals{$reject_name = "${rej_type}rejectsize" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$eto}{$efrom}++; - - } elsif ($p1 =~ /^<(.*?)>: Temporary lookup failure;/) { - $Totals{$reject_name = "${rej_type}rejectlookupfailure" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost}{$eto}{$efrom}++; - - # This would capture all other rejects, but I think it might be more useful to add - # additional capture sections based on user reports of uncapture lines. - # - #} elsif ( ($reason) = ($p1 =~ /^([^;]+);/)) { - # $Totals{$rej_type . 'rejectother'}++; - # $Counts{$rej_type . 'rejectother'}{$reason}++; - } else { - inc_unmatched('rejectother'); - } - } - # end of $re_QID: reject: - -# QID: ACTION STAGE from host[hostip]: trigger: reason; ftph -# -#TDsdN warn: RCPT from host[10.0.0.1]: TEST access WARN action; from=<f@sample.com> to=<eto@example.com> proto=ESMTP helo=<sample.com> -#TDsdN warn: RCPT from host[10.0.0.1]: ; from=<f@sample.com> to=<eto@example.com> proto=ESMTP helo=<sample.net> -#TDsdN discard: RCPT from host[10.0.0.1]: <from@example.com>: Sender address TEST DISCARD action; from=<f@sample.com> to=<eto@example.com> proto=ESMTP helo=<sample.com> -#TDsdN discard: RCPT from host[10.0.0.1]: <host[10.0.0.1]>: Client host TEST DISCARD action w/ip(client_checks); from=<f@sample.com> to=<eto@example.com> proto=ESMTP helo=<sample.com> -#TDsdN discard: RCPT from host[10.0.0.1]: <host[10.0.0.1]>: Unverified Client host triggers DISCARD action; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<10.0.0.1> -#TDsdN hold: RCPT from host[10.0.0.1]: <eto@example.com>: Recipient address triggers HOLD action; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<10.0.0.1> -#TDsdN hold: RCPT from host[10.0.0.1]: <dummy>: Helo command optional text...; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<dummy> -#TDsdN hold: RCPT from host[10.0.0.1]: <dummy>: Helo command triggers HOLD action; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<dummy> -#TDsdN hold: DATA from host[10.0.0.1]: <dummy>: Helo command triggers HOLD action; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<dummy> -#TDsdN filter: RCPT from host[10.0.0.1]: <>: Sender address triggers FILTER filter:somefilter; from=<> to=<eto@example.com> proto=SMTP helo=<sample.com> -#TDsdN filter: RCPT from host[10.0.0.1]: <eto@example.com>: Recipient address triggers FILTER smtp-amavis:[127.0.0.1]:10024; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<sample.net> -#TDsdN redirect: RCPT from host[10.0.0.1]: <example.com[10.0.0.1]>: Client host triggers REDIRECT root@localhost; from=<f@sample.net> to=<eto@example.com> proto=SMTP helo=<localhost> -#TDsdN redirect: RCPT from host[10.0.0.1]: <eto@example.com>: Recipient address triggers REDIRECT root@localhost; from=<f@sample.net> to=<eto@example.com> proto=ESMTP helo=<sample.com> - -# BCC action (postfix 2.6+) -#TDsdN bcc: RCPT from host[10.0.0.1]: <user@example.com>: Sender address triggers BCC root@localhost; from=<f@sample.net> to=<eto@sample.com> proto=ESMTP helo=<sample.net> - - # $re_QID: discard, filter, hold, redirect, warn, bcc, replace ... - else { - my $trigger; - ($trigger,$reason) = ($p1 =~ /^(?:<(\S*)>: )?(.*);$/ ); - if ($trigger eq '') { $trigger = '*unavailable'; } - else { $trigger =~ s/^<(.+)>$/$1/; } - $reason = '*unavailable' if ($reason eq ''); - $fmthost = formathost ($hostip,$host); - #print "trigger: \"$trigger\", reason: \"$reason\"\n"; - - # reason -> subject text - # subject -> "Helo command" : smtpd_helo_restrictions - # subject -> "Client host" : smtpd_client_restrictions - # subject -> "Unverified Client host" : smtpd_client_restrictions - # subject -> "Client certificate" : smtpd_client_restrictions - # subject -> "Sender address" : smtpd_sender_restrictions - # subject -> "Recipient address" : smtpd_recipient_restrictions - - # subject -> "Data command" : smtpd_data_restrictions - # subject -> "End-of-data" : smtpd_end_of_data_restrictions - # subject -> "Etrn command" : smtpd_etrn_restrictions - - # text -> triggers <ACTION> action|triggers <ACTION> <destination>|optional text... - - my ($subject, $text) = - ($reason =~ /^((?:Recipient|Sender) address|(?:Unverified )?Client host|Client certificate|(?:Helo|Etrn|Data) command|End-of-data) (.+)$/o); - #printf "ACTION: '$action', SUBJECT: %-30s TEXT: \"$text\"\n", '"' . $subject . '"'; - - if ($action eq 'filter') { - $Totals{'filtered'}++; next unless ($Collecting{'filtered'}); - # See "Note: Counts" before changing $Counts below re: Filtered - $text =~ s/triggers FILTER //; - if ($subject eq 'Recipient address') { $Counts{'filtered'}{$text}{$subject}{$trigger}{$efrom}{$fmthost}++; } - elsif ($subject =~ /Client host$/) { $Counts{'filtered'}{$text}{$subject}{$fmthost}{$eto}{$efrom}++; } - else { $Counts{'filtered'}{$text}{$subject}{$trigger}{$eto}{$fmthost}++; } - } - elsif ($action eq 'redirect') { - $Totals{'redirected'}++; next unless ($Collecting{'redirected'}); - $text =~ s/triggers REDIRECT //; - # See "Note: Counts" before changing $Counts below re: Redirected - if ($subject eq 'Recipient address') { $Counts{'redirected'}{$text}{$subject}{$trigger}{$efrom}{$fmthost}++; } - elsif ($subject =~ /Client host$/) { $Counts{'redirected'}{$text}{$subject}{$fmthost}{$eto}{$efrom}++; } - else { $Counts{'redirected'}{$text}{$subject}{$trigger}{$eto}{$fmthost}++; } - } - # hold, discard, and warn allow "optional text" - elsif ($action eq 'hold') { - $Totals{'hold'}++; next unless ($Collecting{'hold'}); - # See "Note: Counts" before changing $Counts below re: Hold - $subject = $reason unless $text eq 'triggers HOLD action'; - if ($subject eq 'Recipient address') { $Counts{'hold'}{$subject}{$trigger}{$efrom}{$fmthost}++; } - elsif ($subject =~ /Client host$/) { $Counts{'hold'}{$subject}{$fmthost}{$eto}{$efrom}++; } - else { $Counts{'hold'}{$subject}{$trigger}{$eto}{$fmthost}++; } - } - elsif ($action eq 'discard') { - $Totals{'discarded'}++; next unless ($Collecting{'discarded'}); - # See "Note: Counts" before changing $Counts below re: Discarded - $subject = $reason unless $text eq 'triggers DISCARD action'; - if ($subject eq 'Recipient address') { $Counts{'discarded'}{$subject}{$trigger}{$efrom}{$fmthost}++; } - elsif ($subject =~ /Client host$/) { $Counts{'discarded'}{$subject}{$fmthost}{$eto}{$efrom}++; } - else { $Counts{'discarded'}{$subject}{$trigger}{$eto}{$fmthost}++; } - } - elsif ($action eq 'warn') { - $Totals{'warned'}++; next unless ($Collecting{'warned'}); - $Counts{'warned'}{$reason}{$fmthost}{$eto}{''}++; - # See "Note: Counts" before changing $Counts above... - } - elsif ($action eq 'bcc') { - $Totals{'bcced'}++; next unless ($Collecting{'bcced'}); - # See "Note: Counts" before changing $Counts below re: Filtered - $text =~ s/triggers BCC //o; - if ($subject eq 'Recipient address') { $Counts{'bcced'}{$text}{$subject}{$trigger}{$efrom}{$fmthost}++; } - elsif ($subject =~ /Client host$/) { $Counts{'bcced'}{$text}{$subject}{$fmthost}{$eto}{$efrom}++; } - else { $Counts{'bcced'}{$text}{$subject}{$trigger}{$eto}{$fmthost}++; } - } - else { - die "Unexpected ACTION: '$action'"; - } - } - } - - elsif ($p1 =~ s/^client=(([^ ]*)\[([^ ]*)\](?::(?:\d+|unknown))?)//) { - my ($hip,$host,$hostip) = ($1,$2,$3); - - # Increment accepted when the client connection is made and smtpd has a QID. - # Previously, accepted was being incorrectly incremented when the first qmgr - # "from=xxx, size=nnn ..." line was seen. This is erroneous when the smtpd - # client connection occurred outside the date range of the log being analyzed. - $AcceptedByQid{$qid} = $hip; - $Totals{'msgsaccepted'}++; - - #TDsdQ client=unknown[192.168.0.1] - #TDsdQ client=unknown[192.168.0.1]:unknown - #TDsdQ client=unknown[192.168.0.1]:10025 - #TDsd client=example.com[192.168.0.1], helo=example.com - #TDsdQ client=mail.example.com[2001:dead:beef::1] - - #TDsdQ client=localhost[127.0.0.1], sasl_sender=someone@example.com - #TDsdQ client=example.com[192.168.0.1], sasl_method=PLAIN, sasl_username=anyone@sample.net - #TDsdQ client=example.com[192.168.0.1], sasl_method=LOGIN, sasl_username=user@example.com, sasl_sender=<id352ib@sample.net> - #TDsdQ client=unknown[10.0.0.1], sasl_sender=user@examine.com - next if ($p1 eq ''); - my ($method,$user,$sender) = ($p1 =~ /^(?:, sasl_method=([^,]+))?(?:, sasl_username=([^,]+))?(?:, sasl_sender=<?(.*)>?)?$/); - - # sasl_sender occurs when AUTH verb is present in MAIL FROM, typically used for relaying - # the username (eg. sasl_username) of authenticated users. - if ($sender or $method or $user) { - $Totals{'saslauth'}++; next unless ($Collecting{'saslauth'}); - $method ||= '*unknown method'; - $user ||= '*unknown user'; - $Counts{'saslauth'}{$user . ($sender ? " ($sender)" : '')}{$method}{formathost($hostip,$host)}++; - } - } - - # ^$re_QID: ... (not access(5) action) - elsif ($p1 =~ /^from=<(.*?)>, size=(\d+), nrcpt=(\d+)/) { - my ($efrom,$bytes,$nrcpt) = ($1,$2,$3); - #TDsdQ from=<FROM: SOME USER@example.com>, size=4051, nrcpt=1 (queue active) - #TDsdQ(12) from=<anyone@example.com>, size=25302, nrcpt=2 (queue active) - #TDsdQ from=<from@example.com>, size=5529, nrcpt=1 (queue active) - #TDsdQ from=<from@example.net, @example.com>, size=5335, nrcpt=1 (queue active) - - # Distinguish bytes accepted vs. bytes delivered due to multiple recips - - # Increment bytes accepted on the first qmgr "from=..." line... - next if (exists $SizeByQid{$qid}); - $SizeByQid{$qid} = $bytes; - # ...but only when the smtpd "client=..." line has been seen too. - # This under-counts when the smtpd "client=..." connection log entry and the - # qmgr "from=..." log entry span differnt periods (as fed to postfix-logwatch). - next if (! exists $AcceptedByQid{$qid}); - - $Totals{'bytesaccepted'} += $bytes; - - $Counts{'envelopesenders'}{$efrom ne '' ? $efrom : '<>'}++ if ($Collecting{'envelopesenders'}); - if ($Collecting{'envelopesenderdomains'}) { - my ($localpart, $domain); - if ($efrom eq '') { ($localpart, $domain) = ('<>', '*unknown'); } - else { ($localpart, $domain) = split (/@/, lc $efrom); } - - $Counts{'envelopesenderdomains'}{$domain ne '' ? $domain : '*unknown'}{$localpart}++; - } - delete $AcceptedByQid{$qid}; # prevent incrementing BytesAccepted again - } - - ### sent, forwarded, bounced, softbounce, deferred, (un)deliverable - elsif ($p1 =~ s/^to=<(.*?)>,(?: orig_to=<(.*?)>,)? relay=([^,]*).*, ($re_DDD), status=(\S+) //o) { - ($relay,$status) = ($3,$5); - - my ($to,$origto,$localpart,$domainpart,$dsn,$p1) = process_delivery_attempt ($1,$2,$4,$p1); - - #TD 552B6C20E: to=<to@sample.com>, relay=mail.example.net[10.0.0.1]:25, delay=1021, delays=1020/0.04/0.56/0.78, dsn=2.0.0, status=sent (250 Ok: queued as 6EAC4719EB) - #TD 552B6C20E: to=<to@sample.com>, relay=mail.example.net[10.0.0.1]:25, conn_use=2 delay=1021, delays=1020/0.04/0.56/0.78, dsn=2.0.0, status=sent (250 Ok: queued as 6EAC4719EB) - #TD DD925BBE2: to=<to@example.net>, orig_to=<to-ext@example.net>, relay=mail.example.net[2001:dead:beef::1], delay=2, status=sent (250 Ok: queued as 5221227246) - - ### sent - if ($status eq 'sent') { - if ($p1 =~ /forwarded as /) { - $Totals{'bytesforwarded'} += $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'forwarded'}++; next unless ($Collecting{'forwarded'}); - $Counts{'forwarded'}{$domainpart}{$localpart}{$origto}++; - } - else { - if ($service_name eq 'lmtp') { - $Totals{'bytessentlmtp'} += $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'sentlmtp'}++; next unless ($Collecting{'sentlmtp'}); - $Counts{'sentlmtp'}{$domainpart}{$localpart}{$origto}++; - } - elsif ($service_name eq 'smtp') { - $Totals{'bytessentsmtp'} += $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'sent'}++; next unless ($Collecting{'sent'}); - $Counts{'sent'}{$domainpart}{$localpart}{$origto}++; - } - # virtual, command, ... - else { - $Totals{'bytesdelivered'} += $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'delivered'}++; next unless ($Collecting{'delivered'}); - $Counts{'delivered'}{$domainpart}{$localpart}{$origto}++; - } - } - } - - elsif ($status eq 'deferred') { - #TDsQ to=<to@example.com>, relay=none, delay=27077, delays=27077/0/0.57/0, dsn=4.4.3, status=deferred (Host or domain name not found. Name service error for name=example.com type=MX: Host not found, try again) - #TDsQ to=<to@example.com>, relay=none, delay=141602, status=deferred (connect to mx1.example.com[10.0.0.1]: Connection refused) - #TDsQ to=<to@example.com>, relay=none, delay=141602, status=deferred (delivery temporarily suspended: connect to example.com[192.168.0.1]: Connection refused) - #TDsQ to=<to@example.com>, relay=none, delay=306142, delays=306142/0.04/0.18/0, dsn=4.4.1, status=deferred (connect to example.com[10.0.0.1]: Connection refused) - #TDsQ to=<to@example.org>, relay=example.org[10.0.0.1], delay=48779, status=deferred (lost connection with mail.example.org[10.0.0.1] while sending MAIL FROM) - #TDsQ to=<to@sample.net>, relay=sample.net, delay=26541, status=deferred (conversation with mail.example.com timed out while sending end of data -- message may be sent more than once) - #TDsQ to=<to@sample.net>, relay=sample.net[10.0.0.1]:25, delay=322, delays=0.04/0/322/0, dsn=4.4.2, status=deferred (conversation with example.com[10.0.0.01] timed out while receiving the initial server greeting) - #TDsQ to=<to@localhost>, orig_to=<toalias@localhost>, relay=none, delay=238024, status=deferred (delivery temporarily suspended: transport is unavailable) - - # XXX postfix reports dsn=5.0.0, host's reply may contain its own dsn's such as 511 and #5.1.1 - # XXX should these be used instead? - #TDsQ to=<to@sample.net>, relay=sample.net[10.0.0.1]:25, delay=5.7, delays=0.05/0.02/5.3/0.3, dsn=4.7.1, status=deferred (host sample.net[10.0.0.1] said: 450 4.7.1 <to@sample.net>: Recipient address rejected: Greylisted (in reply to RCPT TO command)) - #TDsQ to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=79799, delays=79797/0.02/0.4/1.3, dsn=4.0.0, status=deferred (host example.com[10.0.0.1] said: 450 <to@example.com>: User unknown in local recipient table (in reply to RCPT TO command)) - #TDsQ to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=97, delays=0.03/0/87/10, dsn=4.0.0, status=deferred (host example.com[10.0.0.1] said: 450 <to@example.com>: Recipient address rejected: undeliverable address: User unknown in virtual alias table (in reply to RCPT TO command)) - - ($reply,$fmthost) = cleanhostreply($p1,$relay,$to,$domainpart); - - $Totals{'deferred'}++ if ($DeferredByQid{$qid}++ == 0); - $Totals{'deferrals'}++; next unless ($Collecting{'deferrals'}); - $Counts{'deferrals'}{get_dsn_msg($dsn)}{$reply}{$domainpart}{$localpart}{$fmthost}++; - } - - ### bounced - elsif ($status eq 'bounced' or $status eq 'SOFTBOUNCE') { - # local agent - #TDlQ to=<envto@example.com>, relay=local, delay=2.5, delays=2.1/0.22/0/0.21, dsn=5.1.1, status=bounced (unknown user: "friend") - - # smtp agent - #TDsQ to=<envto@example.com>, orig_to=<envto>, relay=sample.net[10.0.0.1]:25, delay=22, delays=0.02/0.09/22/0.07, dsn=5.0.0, status=bounced (host sample.net[10.0.0.1] said: 551 invalid address (in reply to MAIL FROM command)) - - #TDsQ to=<envto@example.com>, relay=sample.net[10.0.0.1]:25, delay=11, delays=0.13/0.07/0.98/0.52, dsn=5.0.0, status=bounced (host sample.net[10.0.0.1] said: 550 MAILBOX NOT FOUND (in reply to RCPT TO command)) - #TDsQ to=<envto@example.com>, orig_to=<envto>, relay=sample.net[10.0.0.1]:25, delay=22, delays=0.02/0.09/22/0.07, dsn=5.0.0, status=bounced (host sample.net[10.0.0.1] said: 551 invalid address (in reply to MAIL FROM command)) - - - #TDsQ to=<envto@example.com>, relay=none, delay=0.57, delays=0.57/0/0/0, dsn=5.4.6, status=bounced (mail for sample.net loops back to myself) - #TDsQ to=<>, relay=none, delay=1, status=bounced (mail for sample.net loops back to myself) - #TDsQ to=<envto@example.com>, relay=none, delay=0, status=bounced (Host or domain name not found. Name service error for name=unknown.com type=A: Host not found) - # XXX verify these... - #TD EB0B8770: to=<to@example.com>, orig_to=<postmaster>, relay=none, delay=1, status=bounced (User unknown in virtual alias table) - #TD EB0B8770: to=<to@example.com>, orig_to=<postmaster>, relay=sample.net[192.168.0.1], delay=1.1, status=bounced (User unknown in relay recipient table) - #TD D8962E54: to=<anyone@example.com>, relay=local, conn_use=2 delay=0.21, delays=0.05/0.02/0/0.14, dsn=4.1.1, status=SOFTBOUNCE (unknown user: "to") - #TD F031C832: to=<to@sample.net>, orig_to=<alias@sample.net>, relay=local, delay=0.17, delays=0.13/0.01/0/0.03, dsn=5.1.1, status=bounced (unknown user: "to") - - #TD C76431E2: to=<login@sample.net>, relay=local, delay=2, status=SOFTBOUNCE (host sample.net[192.168.0.1] said: 450 <login@sample.com>: User unknown in local recipient table (in reply to RCPT TO command)) - #TD 04B0702E: to=<anyone@example.com>, relay=example.com[10.0.0.1]:25, delay=12, delays=6.5/0.01/0.03/5.1, dsn=5.1.1, status=bounced (host example.com[10.0.0.1] said: 550 5.1.1 User unknown (in reply to RCPT TO command)) - #TD 9DAC8B2D: to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=1.4, delays=0.04/0/0.27/1.1, dsn=5.0.0, status=bounced (host example.com[10.0.0.1] said: 511 sorry, no mailbox here by that name (#5.1.1 - chkuser) (in reply to RCPT TO command)) - #TD 79CB702D: to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=0.3, delays=0.04/0/0.61/0.8, dsn=5.0.0, status=bounced (host example.com[10.0.0.1] said: 550 <to@example.com>, Recipient unknown (in reply to RCPT TO command)) - #TD 88B7A079: to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=45, delays=0.03/0/5.1/40, dsn=5.0.0, status=bounced (host example.com[10.0.0.1] said: 550-"The recipient cannot be verified. Please check all recipients of this 550 message to verify they are valid." (in reply to RCPT TO command)) - #TD 47B7B074: to=<to@example.com>, relay=example.com[10.0.0.1]:25, delay=6.6, delays=6.5/0/0/0.11, dsn=5.1.1, status=bounced (host example.com[10.0.0.1] said: 550 5.1.1 <to@example.com> User unknown; rejecting (in reply to RCPT TO command)) - #TDppQ to=<withheld>, relay=dbmail-pipe, delay=0.15, delays=0.09/0.01/0/0.06, dsn=5.3.0, status=bounced (Command died with signal 11: "/usr/sbin/dbmail-smtp") - - # print "bounce message from " . $to . " msg : " . $relay . "\n"; - - # See same code elsewhere "Note: Bounce" - ### local bounce - # XXX local v. remote bounce seems iffy, relative - if ($relay =~ /^(?:none|local|virtual|127\.0\.0\.1|maildrop|avcheck)/) { - $Totals{'bouncelocal'}++; next unless ($Collecting{'bouncelocal'}); - $Counts{'bouncelocal'}{get_dsn_msg($dsn)}{$domainpart}{ucfirst($p1)}{$localpart}++; - } - else { - $Totals{'bounceremote'}++; next unless ($Collecting{'bounceremote'}); - ($reply,$fmthost) = cleanhostreply($p1,$relay,$to,$domainpart); - $Counts{'bounceremote'}{get_dsn_msg($dsn)}{$domainpart}{$localpart}{$fmthost}{$reply}++; - } - } - - - elsif ($status =~ 'undeliverable') { - #TDsQ to=<u@example.com>, relay=sample.com[10.0.0.1], delay=0, dsn=5.0.0, status=undeliverable (host sample.com[10.0.0.1] refused to talk to me: 554 5.7.1 example.com Connection not authorized) - #TDsQ to=<to@example.com>, relay=mx.example.com[10.0.0.1]:25, conn_use=2, delay=5.5, delays=0.03/0/0.21/5.3, dsn=5.0.0, status=undeliverable-but-not-cached (host mx.example.com[10.0.0.1] said: 550 RCPT TO:<to@example.com> User unknown (in reply to RCPT TO command)) - #TDvQ to=<u@example.com>, relay=virtual, delay=0.14, delays=0.06/0/0/0.08, dsn=5.1.1, status=undeliverable (unknown user: "u@example.com") - #TDlQ to=<to@example.com>, relay=local, delay=0.02, delays=0.01/0/0/0, dsn=5.1.1, status=undeliverable-but-not-cached (unknown user: "to") - $Totals{'undeliverable'}++; next unless ($Collecting{'undeliverable'}); - if ($p1 =~ /^unknown user: ".+?"$/) { - $Counts{'undeliverable'}{get_dsn_msg($dsn)}{'Unknown user'}{$domainpart}{$localpart}{$origto ? $origto : ''}++; - } - else { - my ($reply,$fmthost) = cleanhostreply($p1,'',$to ne '' ? $to : '<>',$domainpart); - $Counts{'undeliverable'}{get_dsn_msg($dsn)}{$reply}{$domainpart}{$localpart}{$fmthost}++; - } - } - - elsif ($status eq 'deliverable') { - # address verification, sendmail -bv deliverable reports - #TDvQ to=<u@example.com>, relay=virtual, delay=0.09, delays=0.03/0/0/0.06, dsn=2.0.0, status=deliverable (delivers to maildir) - $Totals{'deliverable'}++; next unless ($Collecting{'deliverable'}); - my $dsn = ($p1 =~ s/^($re_DSN) // ? $1 : '*unavailable'); - $Counts{'deliverable'}{$dsn}{$p1}{$origto ? "$to ($origto)" : $to}++; - } - - else { - # keep this as the last condition in this else clause - inc_unmatched('unknownstatus'); - } - } # end of sent, forwarded, bounced, softbounce, deferred, (un)deliverable - - # pickup - elsif ($p1 =~ /^(uid=\S* from=<.*?>)/) { - #TDpQ2 uid=0 from=<root> - $AcceptedByQid{$qid} = $1; - $Totals{'msgsaccepted'}++; - } - - elsif ($p1 =~ /^from=<(.*?)>, status=expired, returned to sender$/) { - #TDqQ from=<from@example.com>, status=expired, returned to sender - $Totals{'returnedtosender'}++; next unless ($Collecting{'returnedtosender'}); - $Counts{'returnedtosender'}{$1 ne '' ? $1 : '<>'}++; - } - - elsif ($p1 =~ s/^host ([^[]+)\[([^]]+)\](?::\d+)? refused to talk to me://) { - #TDsQ host mail.example.com[10.0.0.1] refused to talk to me: 553 Connections are being blocked due to previous incidents of abuse - #TDsQ host mail.example.com[10.0.0.1] refused to talk to me: 501 Connection from 192.168.2.1 (XY) rejected - # Note: See ConnectToFailure above - $Totals{'connecttofailure'}++; next unless ($Collecting{'connecttofailure'}); - $Counts{'connecttofailure'}{'Refused to talk to me'}{formathost($2,$1)}{$p1}++; - } - - elsif ($p1 =~ /^lost connection with ([^[]*)\[([^]]+)\](?::\d+)? (while .*)$/) { - # outbound smtp - #TDsQ lost connection with sample.net[10.0.0.1] while sending MAIL FROM - #TDsQ lost connection with sample.net[10.0.0.2] while receiving the initial server greeting - $Totals{'connectionlostoutbound'}++; next unless ($Collecting{'connectionlostoutbound'}); - $Counts{'connectionlostoutbound'}{ucfirst($3)}{formathost($2,$1)}++; - } - - elsif ($p1 =~ /^conversation with ([^[]*)\[([^]]+)\](?::\d+)? timed out (while .*)$/) { - #TDsQ conversation with sample.net[10.0.0.1] timed out while receiving the initial SMTP greeting - # Note: see TimeoutInbound below - $Totals{'timeoutinbound'}++; next unless ($Collecting{'timeoutinbound'}); - $Counts{'timeoutinbound'}{ucfirst($3)}{formathost($2,$1)}{''}++; - } - - elsif ($p1 =~ /^enabling PIX (<CRLF>\.<CRLF>) workaround for ([^[]+)\[([^]]+)\](?::\d+)?/ or - $p1 =~ /^enabling PIX workarounds: (.*) for ([^[]+)\[([^]]+)\](?::\d+)?/) { - #TDsQ enabling PIX <CRLF>.<CRLF> workaround for example.com[192.168.0.1] - #TDsQ enabling PIX <CRLF>.<CRLF> workaround for mail.sample.net[10.0.0.1]:25 - #TDsQ enabling PIX workarounds: disable_esmtp delay_dotcrlf for spam.example.org[10.0.0.1]:25 - $Totals{'pixworkaround'}++; next unless ($Collecting{'pixworkaround'}); - $Counts{'pixworkaround'}{$1}{formathost($3,$2)}++; - } - - # milter-reject, milter-hold, milter-discard - elsif ($p1 =~ s/^milter-//) { - milter_common($p1); - } - - elsif ($p1 =~ s/^SASL (\[CACHED\] )?authentication failed; //) { - #TDsQ SASL authentication failed; cannot authenticate to server smtp.example.com[10.0.0.1]: no mechanism available - #TDsQ SASL authentication failed; server example.com[10.0.0.1] said: 535 Error: authentication failed - #TDsQ SASL [CACHED] authentication failed; server example.com[10.0.0.1] said: 535 Error: authentication failed - # see saslauthfail elsewhere - - $Totals{'saslauthfail'}++; next unless ($Collecting{'saslauthfail'}); - my $cached = $1; - - if ($p1 =~ /^(authentication protocol loop with server): ([^[]+)\[([^]]+)\](?::\d+)?$/) { - ($reason,$host,$hostip,$reason2) = ($1,$2,$3,''); - } - elsif ($p1 =~ /^(cannot authenticate to server) ([^[]+)\[([^]]+)\](?::\d+)?: (.*)$/) { - ($reason,$host,$hostip,$reason2) = ($1,$2,$3,$4); - } - elsif ($p1 =~ /^server ([^[]+)\[([^]]+)\](?::\d+)? said: (.+)$/) { - ($reason,$host,$hostip,$reason2) = ('server ... said',$1,$2,$3); - } - else { - inc_unmatched('saslauthfail'); - next; - } - - $reason .= ': ' . $reason2 if $reason2; - $Counts{'saslauthfail'}{$cached . $reason}{formathost($hostip,$host)}++; - } - - else { - # keep this as the last condition in this else clause - inc_unmatched('unknownqid') if ! in_ignore_list ($p1); - } - } - # end of $re_QID section - - elsif ($p1 =~ /^(timeout|lost connection) (after [^ ]+)(?: \((?:approximately )?(\d+) bytes\))? from ([^[]*)\[([^]]+)\](?::\d+)?$/) { - my ($lort,$reason,$bytes,$host,$hostip) = ($1,$2,$3,$4,$5); - if ($lort eq 'timeout') { - # see also TimeoutInbound in $re_QID section - #TDsd timeout after RSET from example.com[192.168.0.1] - #TDsd timeout after DATA (6253 bytes) from example.com[10.0.0.1] - - $Totals{'timeoutinbound'}++; next unless ($Collecting{'timeoutinbound'}); - $Counts{'timeoutinbound'}{ucfirst($reason)}{formathost($hostip,$host)}{commify($bytes)}++; - } else { - #TDsd lost connection after CONNECT from mail.example.com[192.168.0.1] - # postfix 2.5:20071003 - #TDsd lost connection after DATA (494133 bytes) from localhost[127.0.0.1] - # postfix 2.6:20080621 - #TDsd lost connection after DATA (approximately 0 bytes) from example.com[10.0.0.1] - - $Totals{'connectionlostinbound'}++; next unless ($Collecting{'connectionlostinbound'}); - $Counts{'connectionlostinbound'}{ucfirst($reason)}{formathost($hostip,$host)}{commify($bytes)}++; - } - } - - elsif ($p1 =~ /^(reject(?:_warning)?): RCPT from ([^[]+)\[([^]]+)\](?::\d+)?: ($re_DSN) Service (?:temporarily )?(?:unavailable|denied)[^;]*; (?:(?:Unverified )?Client host |Sender address |Helo command )?\[[^ ]*\] blocked using ([^;]+);/o) { - my ($rej_type,$host,$hostip,$dsn,) = ($1,$2,$3,$4); - ($site,$reason) = ($5 =~ /^(.+?)(?:$|(?:[.,] )(.*))/); - $reason =~ s/^reason: // if ($reason); - $rej_type = ($rej_type =~ /_warning/ ? 'warn' : get_reject_key($dsn)); - #print "REJECT RBL NOQ: '$rej_type'\n"; - # Note: similar code above: search RejectRBL - - # This section required: postfix didn't always log QID (eg. postfix 1.1) - # Also, "reason:" was probably always present in this case, but I'm not certain - # postfix 1.1 - #TDsd reject_warning: RCPT from example.com[10.0.0.1]: 554 Service unavailable; [10.0.0.1] blocked using orbz.org, reason: Open relay. Please see http://orbz.org/?10.0.0.1; from=<from@example.com> to=<to@sample.net> - #TDsd reject: RCPT from example.com[10.0.0.2]: 554 Service unavailable; [10.0.0.2] blocked using orbz.org, reason: Open relay. Please see http://orbz.org/?10.0.0.2; from=<from@example.com> to=<to@example.net> - #TDsd reject: RCPT from unknown[10.0.0.3]: 554 Service unavailable; [10.0.0.3] blocked using bl.spamcop.net, reason: Blocked - see http://www.spamcop.net/bl.shtml?10.0.0.3; from=<from@example.net> to=<to@example.com> - #TDsd reject: RCPT from example.com[10.0.0.4]: 554 Service unavailable; [10.0.0.4] blocked using sbl.spamhaus.org, reason: http://www.spamhaus.org/SBL/sbl.lasso?query=B12057; from=<from@example.net> to=<to@example.com> - - if ($Collecting{'byiprejects'} and substr($rej_type,0,1) eq '5') { - $fmthost = formathost($hostip,$host); - $Counts{'byiprejects'}{$fmthost}++; - } - - $Totals{$reject_name = "${rej_type}rejectrbl" }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$site}{$fmthost ? $fmthost : formathost($hostip,$host)}{$reason ? $reason : ''}++; - } - - # proxy-reject, proxy-accept - elsif ($p1 =~ s/^proxy-(reject|accept): ([^:]+): //) { - # 2.7 - #TDsdN proxy-accept: END-OF-MESSAGE: 250 2.0.0 Ok: queued as 9BE3547AFE; from=<senderexample.com> to=<recipientexample.com> proto=ESMTP helo=<client.example.com> - #TDsdN proxy-reject: END-OF-MESSAGE: 554 5.7.0 Reject, id=11912-03 - INFECTED: Eicar-Test-Signature; from=<root@example.com> to=<root@example.net> proto=ESMTP helo=<example.com> - #TDsdN proxy-reject: END-OF-MESSAGE: ; from=<user@example.com> to=<user@example.org> proto=SMTP helo=<mail.example.net> - - next if $1 eq 'accept'; #ignore accepts - - my ($stage) = ($2); - my ($efrom,$eto,$proto,$helo) = strip_ftph($p1); - #print "efrom: '$efrom', eto: '$eto', proto: '$proto', helo: '$helo'\n"; - #print "stage: '$stage', reply: '$p1'\n"; - - my ($dsn,$reject_name); - ($dsn,$reply) = ($1,$2) if $p1 =~ /^($re_DSN) (.*)$/o; - #print " dsn: '$dsn', reply: '$reply', key: ", get_reject_key($dsn), "\n"; - # DSN may not be present. Can occur, for example, when queue file size limit is reached, - # which is logged as a Warning. Ignore these, since they can't be add to any - # reject section (no SMTP reply code). - if (! defined $dsn) { - next; - } - - $Totals{$reject_name = get_reject_key($dsn) . 'rejectproxy' }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$stage}{$reply}{$eto}++; - } - - ### smtpd_tls_loglevel >= 1 - # Server TLS messages - elsif (($status,$host,$hostip,$type) = ($p1 =~ /^(?:(Anonymous|Trusted|Untrusted) )?TLS connection established from ([^[]+)\[([^]]+)\](?::\d+)?: (.*)$/)) { - #TDsd TLS connection established from example.com[192.168.0.1]: TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits) - # Postfix 2.5+: status: Untrusted or Trusted - #TDsd Untrusted TLS connection established from example.com[192.168.0.1]: TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits) - #TDsd Anonymous TLS connection established from localhost[127.0.0.1]: TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits) - - $Totals{'tlsserverconnect'}++; next unless ($Collecting{'tlsserverconnect'}); - $Counts{'tlsserverconnect'}{$status ? "$status: $type" : $type}{formathost($hostip,$host)}++; - } - - # Client TLS messages - elsif ( ($status,$host,$type) = ($p1 =~ /^(?:(Verified|Trusted|Untrusted) )?TLS connection established to ([^ ]*): (.*)$/o)) { - #TD TLS connection established to example.com: TLSv1 with cipher AES256-SHA (256/256 bits) - # Postfix 2.5+: peer verification status: Untrusted, Trusted or Verified when - # server's trust chain is valid and peername is matched - #TD Verified TLS connection established to 127.0.0.1[127.0.0.1]:26: TLSv1 with cipher DHE-DSS-AES256-SHA (256/256 bits) - - $Totals{'tlsclientconnect'}++; next unless ($Collecting{'tlsclientconnect'}); - $Counts{'tlsclientconnect'}{$status ? "$status: $type" : $type}{$host}++; - } - - # smtp_tls_note_starttls_offer=yes - elsif ($p1 =~ /^Host offered STARTTLS: \[(.*)\]$/o) { - #TD Host offered STARTTLS: [mail.example.com] - $Totals{'tlsoffered'}++; next unless ($Collecting{'tlsoffered'}); - $Counts{'tlsoffered'}{$1}++; - } - - ### smtpd_tls_loglevel >= 1 - elsif ($p1 =~ /^Unverified: (.*)/o) { - #TD Unverified: subject_CN=(www|smtp|mailhost).(example.com|sample.net), issuer=someuser - $Totals{'tlsunverified'}++; next unless ($Collecting{'tlsunverified'}); - $Counts{'tlsunverified'}{$1}++; - } - - # Note: no QID - elsif (($host,$hostip,$dsn,$from,$to) = ($p1 =~ /^reject: RCPT from ([^[]+)\[([^]]+)\](?::\d+)?: ([45]52) Message size exceeds fixed limit; from=<(.*?)> to=<(.*?)>/)) { - #TD reject: RCPT from size.example.com[192.168.0.1]: 452 Message size exceeds fixed limit; from=<from@example.com> to=<to@sample.net> - #TD reject: RCPT from size.example.com[192.168.0.1]: 552 Message size exceeds fixed limit; from=<from@example.com> to=<to@sample.net> proto=ESMTP helo=<example.com> - # Note: similar code above: search RejectSize - # Note: reject_warning does not seem to occur - if ($Collecting{'byiprejects'} and substr($dsn,0,1) eq '5') { - $fmthost = formathost($hostip,$host); - $Counts{'byiprejects'}{$fmthost}++; - } - $Totals{$reject_name = get_reject_key($dsn) . 'rejectsize' }++; next unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$fmthost ? $fmthost : formathost($hostip,$host)}{$to}{$from ne '' ? $from : '<>'}++; - } - - elsif ($p1 =~ /looking for plugins in (.*)$/) { - #TD looking for plugins in '/usr/lib/sasl2', failed to open directory, error: No such file or directory - $Totals{'warnconfigerror'}++; next unless ($Collecting{'warnconfigerror'}); - $Counts{'warnconfigerror'}{$1}++; - } - - # SMTP/ESMTP protocol violations - elsif ($p1 =~ /^(improper command pipelining) (after \S+) from ([^[]*)\[([^]]+)\](?::\d+)?/) { - # ProtocolViolation - #TDsd postfix/smtpd[24928]: improper command pipelining after RCPT from unknown[192.168.0.1] - my ($error,$stage,$host,$hostip) = ($1,$2,$3,$4); - $Totals{'smtpprotocolviolation'}++; next unless ($Collecting{'smtpprotocolviolation'}); - $Counts{'smtpprotocolviolation'}{ucfirst($error)}{ucfirst($stage)}{formathost($hostip,$host)}++; - } - - elsif ($p1 =~ /^(too many errors) (after [^ ]*)(?: \((?:approximately )?\d+ bytes\))? from ([^[]*)\[([^]]+)\](?::\d+)?$/) { - my ($error,$stage,$host,$hostip) = ($1,$2,$3,$4); - #TDsd too many errors after AUTH from sample.net[10.0.0.1] - #TDsd too many errors after DATA (0 bytes) from 1-0-0-10.example.com[10.0.0.1] - $Totals{'smtpprotocolviolation'}++; next unless ($Collecting{'smtpprotocolviolation'}); - $Counts{'smtpprotocolviolation'}{ucfirst($error)}{ucfirst($stage)}{formathost($hostip,$host)}++; - } - - # coerce these into general warnings - elsif ( $p1 =~ /^cannot load Certificate Authority data/ or - $p1 =~ /^SSL_connect error to /) - { - #TDsQ Cannot start TLS: handshake failure - #TDsd cannot load Certificate Authority data - #TDs SSL_connect error to mail.example.com: 0 - - postfix_warning($p1); - } - - else { - # add to the unmatched list if not on the ignore_list - inc_unmatched('final') if ! in_ignore_list ($p1); - } -} - -######################################## -# Final tabulations, and report printing - -for my $code (@RejectKeys) { - for my $type (@RejectClasses) { - $Totals{'totalrejects' . $code} += $Totals{$code . $type}; - } - - if ($code =~ /^5/o) { - $Totals{'totalrejects'} += $Totals{'totalrejects' . $code}; - } -} - -# XXX this was naive - the goal was to avoid recounting messages -# released from quarantine, but externally introduced messages may -# contain resent-message-id; trying to track only internally resent -# messages does not seem useful. -# make some corrections now, due to double counting -#$Totals{'msgsaccepted'} -= $Totals{'resent'} if ($Totals{'msgsaccepted'} >= $Totals{'resent'}); - -$Totals{'totalacceptplusreject'} = $Totals{'msgsaccepted'} + $Totals{'totalrejects'}; - -# Print the Summary report if any key has non-zero data. -# Note: must explicitely check for any non-zero data, -# as Totals always has some keys extant. -# -if ($Opts{'summary'}) { - for (keys %Totals) { - if ($Totals{$_}) { - print_summary_report (@Sections); - last; - } - } -} - -# Print the Detail report, if detail is sufficiently high -# -if ($Opts{'detail'} >= 5) { - #print STDERR "Counts memory usage: ", commify(Devel::Size::total_size(\%Counts)), "\n"; - #print STDERR "Delays memory usage: ", commify(Devel::Size::total_size(\%Delays)), "\n"; - print_detail_report(@Sections); - - if ($Opts{'delays'}) { - my @table; - for (sort keys %Delays) { - # anon array ref: label, array ref of $Delay{key} - push @table, [ substr($_,3), $Delays{$_} ]; - } - if (@table) { - print_percentiles_report2(\@table, "Delivery Delays Percentiles", $Opts{'delays_percentiles'}); - } - } - - print_postgrey_reports(); - -} - -# debug: show which ignore_list items are hit most -#my %IGNORED; -#for (sort { $IGNORED{$b} <=> $IGNORED{$a} } keys %IGNORED) { -# printf "%10d: KEY: %s\n", $IGNORED{$_}, $_; -#} - -# Finally, print any unmatched lines -# -print_unmatched_report(); - -# -# End of main -# -################################################## - -# Create the list of REs against which log lines are matched. -# Lines that match any of the patterns in this list are ignored. -# -# Note: This table is created at runtime, due to a Perl bug which -# I reported as perl bug #56202: -# -# http://rt.perl.org/rt3/Public/Bug/Display.html?id=56202 -# -sub create_ignore_list() { - # top 3 hitters up front - push @ignore_list, qr/^statistics:/; - push @ignore_list, qr/^setting up TLS connection (?:from|to)/; - push @ignore_list, qr/^Verified: /; - push @ignore_list, qr/^skipped, still being delivered/; - - # SSL info at/above mail.info level - push @ignore_list, qr/^read from [a-fA-F\d]{8}/; - push @ignore_list, qr/^write to [a-fA-F\d]{8}/; - push @ignore_list, qr/^[a-f\d]{4} [a-f\d]{2}/; - push @ignore_list, qr/^[a-f\d]{4} - <SPACES/; - push @ignore_list, qr/^[<>]+ /; - - push @ignore_list, qr/^premature end-of-input (?:on|from) .* socket while reading input attribute name$/; - push @ignore_list, qr/^certificate peer name verification failed/; - push @ignore_list, qr/^Peer certi?ficate could not be verified$/; # missing i was a postfix typo - push @ignore_list, qr/^Peer cert verify depth=/; - push @ignore_list, qr/^Peer verification:/; - push @ignore_list, qr/^Server certificate could not be verified/; - push @ignore_list, qr/^cannot load .SA certificate and key data/; - push @ignore_list, qr/^tlsmgr_cache_run_event/; - push @ignore_list, qr/^SSL_accept/; - push @ignore_list, qr/^SSL_connect:/; - push @ignore_list, qr/^connection (?:closed|established)/; - push @ignore_list, qr/^delete smtpd session/; - push @ignore_list, qr/^put smtpd session/; - push @ignore_list, qr/^save session/; - push @ignore_list, qr/^Reusing old/; - push @ignore_list, qr/^looking up session/; - push @ignore_list, qr/^lookup smtpd session/; - push @ignore_list, qr/^lookup \S+ type/; - push @ignore_list, qr/^xsasl_(?:cyrus|dovecot)_/; - push @ignore_list, qr/^watchdog_/; - push @ignore_list, qr/^read smtpd TLS/; - push @ignore_list, qr/^open smtpd TLS/; - push @ignore_list, qr/^write smtpd TLS/; - push @ignore_list, qr/^read smtp TLS cache entry/; - push @ignore_list, qr/^starting TLS engine$/; - push @ignore_list, qr/^initializing the server-side TLS/; - push @ignore_list, qr/^global TLS level: /; - push @ignore_list, qr/^auto_clnt_/; - push @ignore_list, qr/^generic_checks:/; - push @ignore_list, qr/^inet_addr_/; - push @ignore_list, qr/^mac_parse:/; - push @ignore_list, qr/^cert has expired/; - push @ignore_list, qr/^daemon started/; - push @ignore_list, qr/^master_notify:/; - push @ignore_list, qr/^rewrite_clnt:/; - push @ignore_list, qr/^rewrite stream/; - push @ignore_list, qr/^dict_/; - push @ignore_list, qr/^send attr /; - push @ignore_list, qr/^match_/; - push @ignore_list, qr/^input attribute /; - push @ignore_list, qr/^Run-time/; - push @ignore_list, qr/^Compiled against/; - push @ignore_list, qr/^private\//; - push @ignore_list, qr/^reject_unknown_/; # don't combine or shorten these reject_ patterns - push @ignore_list, qr/^reject_unauth_/; - push @ignore_list, qr/^reject_non_/; - push @ignore_list, qr/^permit_/; - push @ignore_list, qr/^idle timeout/; - push @ignore_list, qr/^get_dns_/; - push @ignore_list, qr/^dns_/; - push @ignore_list, qr/^chroot /; - push @ignore_list, qr/^process generation/; - push @ignore_list, qr/^fsspace:/; - push @ignore_list, qr/^master disconnect/; - push @ignore_list, qr/^resolve_clnt/; - push @ignore_list, qr/^ctable_/; - push @ignore_list, qr/^extract_addr/; - push @ignore_list, qr/^mynetworks:/; - push @ignore_list, qr/^name_mask:/; - #TDm reload -- version 2.6-20080814, configuration /etc/postfix - #TDm reload configuration /etc/postfix - push @ignore_list, qr/^reload (?:-- version \S+, )?configuration/; - push @ignore_list, qr/^terminating on signal 15$/; - push @ignore_list, qr/^verify error:num=/; - push @ignore_list, qr/^verify return:/; - push @ignore_list, qr/^nss_ldap: /; - push @ignore_list, qr/^discarding EHLO keywords: /; - push @ignore_list, qr/^sql auxprop plugin/; - push @ignore_list, qr/^sql plugin/; - push @ignore_list, qr/^sql_select/; - push @ignore_list, qr/^auxpropfunc error/; - push @ignore_list, qr/^commit transaction/; - push @ignore_list, qr/^begin transaction/; - push @ignore_list, qr/^maps_find: /; - push @ignore_list, qr/^check_access: /; - push @ignore_list, qr/^check_domain_access: /; - push @ignore_list, qr/^check_mail_access: /; - push @ignore_list, qr/^check_table_result: /; - push @ignore_list, qr/^mail_addr_find: /; - push @ignore_list, qr/^mail_addr_map: /; - push @ignore_list, qr/^mail_flow_put: /; - push @ignore_list, qr/^smtp_addr_one: /; - push @ignore_list, qr/^smtp_connect_addr: /; - push @ignore_list, qr/^smtp_connect_unix: trying: /; - push @ignore_list, qr/^smtp_find_self: /; - push @ignore_list, qr/^smtp_get: /; - push @ignore_list, qr/^smtp_fputs: /; - push @ignore_list, qr/^smtp_parse_destination: /; - push @ignore_list, qr/^smtp_sasl_passwd_lookup: /; - push @ignore_list, qr/^smtpd_check_/; - push @ignore_list, qr/^smtpd_chat_notify: /; - push @ignore_list, qr/^been_here: /; - push @ignore_list, qr/^set_eugid: /; - push @ignore_list, qr/^deliver_/; - push @ignore_list, qr/^flush_send_file: queue_id/; - push @ignore_list, qr/^milter_macro_lookup/; - push @ignore_list, qr/^milter8/; - push @ignore_list, qr/^skipping non-protocol event/; - push @ignore_list, qr/^reply: /; - push @ignore_list, qr/^event: /; - push @ignore_list, qr/^trying... /; - push @ignore_list, qr/ all milters$/; - push @ignore_list, qr/^vstream_/; - push @ignore_list, qr/^server features/; - push @ignore_list, qr/^skipping event/; - push @ignore_list, qr/^Using /; - push @ignore_list, qr/^rec_(?:put|get): /; - push @ignore_list, qr/^subject=/; - push @ignore_list, qr/^issuer=/; - push @ignore_list, qr/^pref /; # yes, multiple spaces - push @ignore_list, qr/^request: \d/; - push @ignore_list, qr/^done incoming queue scan$/; - push @ignore_list, qr/^qmgr_/; - push @ignore_list, qr/^trigger_server_accept_fifo: /; - push @ignore_list, qr/^proxymap stream/; - push @ignore_list, qr/^(?:start|end) sorted recipient list$/; - push @ignore_list, qr/^connecting to \S+ port /; - push @ignore_list, qr/^Write \d+ chars/; - push @ignore_list, qr/^Read \d+ chars/; - push @ignore_list, qr/^(?:lookup|delete) smtp session/; - push @ignore_list, qr/^delete smtp session/; - push @ignore_list, qr/^(?:reloaded|remove|looking for) session .* cache$/; - push @ignore_list, qr/^(?:begin|end) \S+ address list$/; - push @ignore_list, qr/^mapping DSN status/; - push @ignore_list, qr/^record [A-Z]/; - push @ignore_list, qr/^dir_/; - push @ignore_list, qr/^transport_event/; - push @ignore_list, qr/^read [A-Z](?: |$)/; - push @ignore_list, qr/^relay: /; - push @ignore_list, qr/^why: /; - push @ignore_list, qr/^fp: /; - push @ignore_list, qr/^path: /; - push @ignore_list, qr/^level: /; - push @ignore_list, qr/^recipient: /; - push @ignore_list, qr/^delivered: /; - push @ignore_list, qr/^queue_id: /; - push @ignore_list, qr/^queue_name: /; - push @ignore_list, qr/^user: /; - push @ignore_list, qr/^sender: /; - push @ignore_list, qr/^offset: /; - push @ignore_list, qr/^offset: /; - push @ignore_list, qr/^verify stream disconnect/; - push @ignore_list, qr/^event_request_timer: /; - push @ignore_list, qr/^smtp_sasl_authenticate: /; - push @ignore_list, qr/^flush_add: /; - push @ignore_list, qr/^disposing SASL state information/; - push @ignore_list, qr/^starting new SASL client/; - push @ignore_list, qr/^error: dict_ldap_connect: /; - push @ignore_list, qr/^error: to submit mail, use the Postfix sendmail command/; - push @ignore_list, qr/^local_deliver[:[]/; - push @ignore_list, qr/^_sasl_plugin_load /; - push @ignore_list, qr/^exp_type: /; - push @ignore_list, qr/^wakeup [\dA-Z]/; - push @ignore_list, qr/^defer (?:site|transport) /; - push @ignore_list, qr/^local: /; - push @ignore_list, qr/^exp_from: /; - push @ignore_list, qr/^extension: /; - push @ignore_list, qr/^owner: /; - push @ignore_list, qr/^unmatched: /; - push @ignore_list, qr/^domain: /; - push @ignore_list, qr/^initializing the client-side TLS engine/; - push @ignore_list, qr/^header_token: /; - push @ignore_list, qr/^(?:PUSH|POP) boundary/; - push @ignore_list, qr/^recipient limit \d+$/; - push @ignore_list, qr/^scan_dir_next: found/; - push @ignore_list, qr/^open (?:btree|incoming)/; - push @ignore_list, qr/^Renamed to match inode number/; - push @ignore_list, qr/^cleanup_[^:]+:/; - push @ignore_list, qr/^(?:before|after) input_transp_cleanup: /; - push @ignore_list, qr/^event_enable_read: /; - push @ignore_list, qr/^report recipient to all milters /; - push @ignore_list, qr/_action = defer_if_permit$/; - push @ignore_list, qr/^reject_invalid_hostname: /; - - # non-anchored - #push @ignore_list, qr/: Greylisted for /; - push @ignore_list, qr/certificate verification (?:depth|failed for)/; - push @ignore_list, qr/re-using session with untrusted certificate, look for details earlier in the log$/; - push @ignore_list, qr/socket: wanted attribute: /; - push @ignore_list, qr/ smtpd cache$/; - push @ignore_list, qr/ old session$/; - push @ignore_list, qr/fingerprint=/; - push @ignore_list, qr/TLS cipher list "/; -} - -# Evaluates a given line against the list of ignore patterns. -# -sub in_ignore_list($) { - my $line = shift; - - foreach (@ignore_list) { - #return 1 if $line =~ /$_/; - if ($line =~ /$_/) { - #$IGNORED{$_}++; - return 1; - } - } - - return 0; -} - -# Accepts common fields from a standard delivery attempt, processing then -# and returning modified values -# -sub process_delivery_attempt ($ $ $ $) { - my ($to,$origto,$DDD,$reason) = @_; - - $reason =~ s/\((.*)\)/$1/; # Makes capturing nested parens easier - # leave $to/$origto undefined, or strip < > chars if not null address (<>). - defined $to and $to = ($to eq '') ? '<>' : lc $to; - defined $origto and $origto = ($origto eq '') ? '<>' : lc $origto; - my ($localpart, $domainpart) = split ('@', $to); - ($localpart, $domainpart) = ($to, '*unspecified') if ($domainpart eq ''); - my ($dsn); - - # If recipient_delimiter is set, break localpart into user + extension - # and save localpart in origto if origto is empty - # - if ($Opts{'recipient_delimiter'} and $localpart =~ /\Q$Opts{'recipient_delimiter'}\E/o) { - - # special cases: never split mailer-daemon or double-bounce - # or owner- or -request if delim is "-" (dash). - unless ($localpart =~ /^(?:mailer-daemon|double-bounce)$/i or - ($Opts{'recipient_delimiter'} eq '-' and $localpart =~ /^owner-.|.-request$/i)) { - my ($user,$extension) = split (/\Q$Opts{'recipient_delimiter'}\E/, $localpart, 2); - $origto = $localpart if ($origto eq ''); - $localpart = $user; - } - } - - unless (($dsn) = ($DDD =~ /dsn=(\d\.\d+\.\d+)/o)) { - $dsn = ''; - } - - if ($Collecting{'delays'} and $DDD =~ m{delay=([\d.]+)(?:, delays=([\d.]+)/([\d.]+)/([\d.]+)/([\d.]+))?}) { - # Message delivery time stamps - # delays=a/b/c/d, where - # a = time before queue manager, including message transmission - # b = time in queue manager - # c = connection setup including DNS, HELO and TLS; - # d = message transmission time. - if (defined $2) { - $Delays{'1: Before qmgr'}{$2}++; - $Delays{'2: In qmgr'}{$3}++; - $Delays{'3: Conn setup'}{$4}++; - $Delays{'4: Transmission'}{$5}++; - } - $Delays{'5: Total'}{$1}++; - } - - return ($to,$origto,$localpart,$domainpart,$dsn,$reason); -} - -# Processes postfix/bounce messages -# -sub postfix_bounce($) { - my $line = shift; - my $type; - - $line =~ s/^(?:$re_QID): //o; - if ($line =~ /^(sender|postmaster) non-delivery notification/o) { - #TDbQ postmaster non-delivery notification: 7446BCD68 - #TDbQ sender non-delivery notification: 7446BCD68 - $type = 'Non-delivery'; - } - elsif ($line =~ /^(sender|postmaster) delivery status notification/o ) { - #TDbQ sender delivery status notification: 7446BCD68 - $type = 'Delivery'; - } - elsif ($line =~ /^(sender|postmaster) delay notification: /o) { - #TDbQ sender delay notification: AA61EC2F9A - $type = 'Delayed'; - } - else { - inc_unmatched('bounce') if ! in_ignore_list($line); - return; - } - - $Totals{'notificationsent'}++; return unless ($Collecting{'notificationsent'}); - $Counts{'notificationsent'}{$type}{$1}++; -} - -# Processes postfix/cleanup messages -# cleanup always has a QID -# -sub postfix_cleanup($) { - my $line = shift; - my ($qid,$reply,$fmthost,$reject_name); - - ($qid, $line) = ($1, $2) if ($line =~ /^($re_QID): (.*)$/o ); - - #TDcQ message-id=<C1BEA2A0.188572%from@example.com> - return if ($line =~ /^message-id=/); - - # milter-reject, milter-hold, milter-discard - if ($line =~ s/^milter-//) { - milter_common($line); - return; - } - - ### cleanup bounced messages (always_bcc, recipient_bcc_maps, sender_bcc_maps) - # Note: Bounce - # See same code elsewhere "Note: Bounce" - #TDcQ to=<envto@example.com>, relay=none, delay=0.11, delays=0.11/0/0/0, dsn=5.7.1, status=bounced optional text... - #TDcQ to=<envto@example.com>, orig_to=<envto>, relay=none, delay=0.13, delays=0.13/0/0/0, dsn=5.7.1, status=bounced optional text... - if ($line =~ /^to=<(.*?)>,(?: orig_to=<(.*?)>,)? relay=([^,]*).*, ($re_DDD), status=([^ ]+) (.*)$/o) { - # ($to,$origto,$relay,$DDD,$status,$reason) = ($1,$2,$3,$4,$5,$6); - if ($5 ne 'bounced' and $5 ne 'SOFTBOUNCE') { - inc_unmatched('cleanupbounce'); - return; - } - - my ($to,$origto,$relay,$DDD,$reason) = ($1,$2,$3,$4,$6); - my ($localpart,$domainpart,$dsn); - ($to,$origto,$localpart,$domainpart,$dsn,$reason) = process_delivery_attempt ($to,$origto,$DDD,$reason); - - ### local bounce - # XXX local v. remote bounce seems iffy, relative - if ($relay =~ /^(?:none|local|virtual|maildrop|127\.0\.0\.1|avcheck)/) { - $Totals{'bouncelocal'}++; return unless ($Collecting{'bouncelocal'}); - $Counts{'bouncelocal'}{get_dsn_msg($dsn)}{$domainpart}{ucfirst($reason)}{$localpart}++; - } - ### remote bounce - else { - ($reply,$fmthost) = cleanhostreply($reason,$relay,$to ne '' ? $to : '<>',$domainpart); - $Totals{'bounceremote'}++; return unless ($Collecting{'bounceremote'}); - $Counts{'bounceremote'}{get_dsn_msg($dsn)}{$domainpart}{$localpart}{$fmthost}{$reply}++; - } - } - - # *header_checks and body_checks - elsif (header_body_checks($line)) { - #print "cleanup: header_body_checks\n"; - return; - } - - #TDcQ resent-message-id=4739073.1 - #TDcQ resent-message-id=<ARF+DXZwLECdxm@mail.example.com> - #TDcQ resent-message-id=<B19-DVD42188E0example.com>? <120B11@samplepc> - elsif ( ($line =~ /^resent-message-id=<?.+>?$/o )) { - $Totals{'resent'}++; - } - - #TDcN unable to dlopen .../sasl2/libplain.so.2: .../sasl2/libplain.so.2: failed to map segment from shared object: Operation not permitted - elsif ($line =~ /^unable to dlopen /) { - # strip extraneous doubling of library path - $line = "$1$2 $3" if ($line =~ /(unable to dlopen )([^:]+: )\2(.+)$/); - postfix_warning($line); - } - - else { - inc_unmatched('cleanup(last)') if ! in_ignore_list($line); - } -} - -=pod - header_body_checks - - Handle cleanup's header_checks and body_checks, and smtp's smtp_body_checks/smtp_*header_checks - - Possible actions that log are: - - REJECT optional text... - DISCARD optional text... - FILTER transport:destination - HOLD optional text... - REDIRECT user@domain - PREPEND text... - REPLACE text... - WARN optional text... - - DUNNO and IGNORE are not logged - -Returns: - 1: if line matched or handled - 0: otherwise -=cut - -sub header_body_checks($) -{ - my $line = shift; - - # bcc, discard, filter, hold, prepend, redirect, reject, replace, warning - return 0 if ($line !~ /^[bdfhprw]/) or # short circuit alternation when no match possible - ($line !~ /^(re(?:ject|direct|place)|filter|hold|discard|prepend|warning|bcc): (header|body|content) (.*)$/); - - my ($action,$part,$p3) = ($1,$2,$3); - - #print "header_body_checks: action: \"$action\", part: \"$part\", p3: \"$p3\"\n"; - - my ($trigger,$host,$eto,$p4,$fmthost,$reject_name); - # $re_QID: reject: body ... - # $re_QID: reject: header ... - # $re_QID: reject: content ... - - - if ($p3 =~ /^(.*) from ([^;]+); from=<.*?>(?: to=<(.*?)>)?(?: proto=\S*)?(?: helo=<.*?>)?(?:: (.*)|$)/) { - ($trigger,$host,$eto,$p4) = ($1,$2,$3,$4); - - # $action $part $trigger $host $eto $p4 - #TDcQ reject: body Subject: Cheap cialis from local; from=<root@localhost>: optional text... - #TDcQ reject: body Quality replica watches!!! from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=SMTP helo=<example.com>: optional text... - #TDcQ reject: header To: <user@example.com> from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: optional text... - # message_reject_characters (postfix >= 2.3) - #TDcQ reject: content Received: by example.com Postfix from example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=.example.com>: 5.7.1 disallowed character - - #TDcQ filter: header To: to@example.com from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: transport:destination - #TDcQ hold: header Message-ID: <user@example.com> from localhost[127.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: optional text... - #TDcQ hold: header Subject: Hold Test from local; from=<efrom@example.com> to=<eto@sample.net>: optional text... - #TDcQ hold: header Received: by example.com...from x from local; from=<efrom@example.com> - #TDcQ hold: header Received: from x.com (x.com[10.0.0.1])??by example.com (Postfix) with ESMTP id 630BF??for <X>; Thu, 20 Oct 2006 13:27: from example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com> - # hold: header Received: from [10.0.0.1] by example.com Thu, 9 Jan 2008 18:06:06 -0500 from sample.net[10.0.0.2]; from=<> to=<to@example.com> proto=SMTP helo=<sample.net>: faked header - #TDcQ redirect: header From: "Attn Men" <attn@example.com> from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: user@domain - #TDcQ redirect: header From: "Superman" <attn@example.com> from hb.example.com[10.0.0.2]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: user@domain - #TDcQ redirect: body Original drugs from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=SMTP helo=<example.com>: user@domain - #TDcQ discard: header Subject: **SPAM** Blah... from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com> - #TDcQ prepend: header Rubble: Mr. from localhost[127.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: text... - #TDcQ replace: header Rubble: flintstone from localhost[127.0.0.1]; from=<efrom@apple.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: text... - #TDcQ warning: header Date: Tues, 99:34:67 from localhost[127.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: optional text... - # BCC action (2.6 experimental branch) - #TDcQ bcc: header To: to@example.com from hb.example.com[10.0.0.1]; from=<efrom@example.com> to=<eto@sample.net> proto=ESMTP helo=<example.com>: user@domain - - # Note: reject_warning does not seem to occur - } - - else { - # smtp_body_checks, smtp_header_checks, smtp_mime_header_checks, smtp_nested_header_checks (postfix >= 2.5) - #TDsQ replace: header Sender: <from@example.com> : Sender: <fm2@sample.net> - - $trigger = $p3; $host = ''; $eto = ''; $p4 = $part eq 'body' ? 'smtp_body_checks' : 'smtp_*header_checks'; - - #inc_unmatched('header_body_checks'); - #return 1; - } - - #print " trigger: \"$trigger\", host: \"$host\", eto: \"$eto\", p4: \"$p4\"\n"; - $trigger =~ s/\s+/ /g; - $trigger = '*unknown reason' if ($trigger eq ''); - $eto = '*unknown' if ($eto eq ''); - - my ($trig,$trig_opt,$text); - if ($part eq 'header') { ($trig = $trigger) =~ s/^([^:]+:).*$/Header check "$1"/; } - elsif ($part eq 'body') { $trig = "Body check"; } - else { $trig = "Content check"; } # message_reject_characters (postfix >= 2.3) - - if ($p4 eq '') { $text = '*generic'; $trig_opt = $trig; } - else { $text = $p4; $trig_opt = "$trig ($p4)"; } - - if ($host eq 'local') { $fmthost = formathost('127.0.0.1', 'local'); } - elsif ($host =~ /([^[]+)\[([^]]+)\]/) { $fmthost = formathost($2,$1); } - else { $fmthost = '*unknown'; } - - # Note: Counts - # Ensure each $Counts{key} accumulator is consistently - # used with the same number of hash key levels throughout the code. - # For example, $Counts{'hold'} below has 4 keys; ensure that every - # other usage of $Counts{'hold'} also has 4 keys. Currently, it is - # OK to set the last key as '', but only the last. - - if ($action eq 'reject') { - $Counts{'byiprejects'}{$fmthost}++ if $Collecting{'byiprejects'}; - - # Note: no temporary or reject_warning - # Note: no reply code - force into a 5xx reject - # XXX this won't be seen if the user has no 5.. entry in reject_reply_patterns - $Totals{$reject_name = "5xxreject$part" }++; - $Counts{$reject_name}{$text}{$eto}{$fmthost}{$trigger}++ if $Collecting{$reject_name}; - } - elsif ( $action eq 'filter' ) { - $Totals{'filtered'}++; - $Counts{'filtered'}{$text}{$trig}{$trigger}{$eto}{$fmthost}++ if $Collecting{'filtered'}; - } - elsif ( $action eq 'hold' ) { - $Totals{'hold'}++; - $Counts{'hold'}{$trig_opt}{$fmthost}{$eto}{$trigger}++ if $Collecting{'hold'}; - } - elsif ( $action eq 'redirect' ) { - $Totals{'redirected'}++; - $Counts{'redirected'}{$trig}{$text}{$eto}{$fmthost}{$trigger}++ if $Collecting{'redirected'}; - } - elsif ( $action eq 'discard' ) { - $Totals{'discarded'}++; - $Counts{'discarded'}{$trig}{$fmthost}{$eto}{$trigger}++ if $Collecting{'discarded'}; - } - elsif ( $action eq 'prepend' ) { - $Totals{'prepended'}++; - $Counts{'prepended'}{"$trig ($text)"}{$fmthost}{$eto}{$trigger}++ if $Collecting{'prepended'}; - } - elsif ( $action eq 'replace' ) { - $Totals{'replaced'}++; - $Counts{'replaced'}{"$trig ($text)"}{$fmthost}{$eto}{$trigger}++ if $Collecting{'replaced'}; - } - elsif ( $action eq 'warning' ) { - $Totals{'warned'}++; - $Counts{'warned'}{$trig}{$fmthost}{$eto}{$trigger}++ if $Collecting{'warned'}; - } - elsif ( $action eq 'bcc' ) { - $Totals{'bcced'}++; - $Counts{'bcced'}{$text}{$trig}{$trigger}{$eto}{$fmthost}++ if $Collecting{'bcced'}; - } - else { - inc_unmatched('header_body_checks unexpected action'); - } - - return 1; -} - - -# Handle common milter actions: -# milter-reject, milter-hold, milter-discard -# which are created by both smtpd and cleanup -# -sub milter_common($) { - my $line = shift; - - #TDsdN milter-reject: MAIL from milterS.example.com[10.0.0.1]: 553 5.1.7 address incomplete; proto=ESMTP helo=<example.com> - #TDsdN milter-reject: CONNECT from milterS.example.com[10.0.0.2]: 451 4.7.1 Service unavailable - try again later; proto=SMTP - #TDsdQ milter-reject: END-OF-MESSAGE from milterS.example.com[10.0.0.3]: 5.7.1 black listed URL host sample.com by ...uribl.com; from=<from@sample.com> to=<to@example.net> proto=ESMTP helo=<example.com> - #TDsdQ milter-hold: END-OF-MESSAGE from milterS.example.com[10.0.0.4]: milter triggers HOLD action; from=<from@sample.com> to=<to@example.net> proto=ESMTP helo=<sample.com> - - #TDcQ milter-reject: END-OF-MESSAGE from milterC.example.com[10.0.0.1]: 5.7.1 Some problem; from=<efrom@example.com> to=<eto@sample.net> proto=SMTP helo=<example.com> - #TDcQ milter-reject: CONNECT from milterC.example.com[10.0.0.2]: 5.7.1 Some problem; proto=SMTP - #TDcQ milter-hold: END-OF-MESSAGE from milterC.example.com[10.0.0.3]: milter triggers HOLD action; from=<efrom@example.com> to=<eto@example.net> proto=ESMTP helo=<example.com> - #TDcQ milter-discard: END-OF-MESSAGE from milterC.example.com[10.0.0.4]: milter triggers DISCARD action; from=<efrom@example.com> to=<eto@example.net> proto=ESMTP helo=<example.com> -# 84B82AC8B3: milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 5.7.1 Blocked - - my ($efrom,$eto,$proto,$helo) = strip_ftph($line); - #print "efrom: '$efrom', eto: '$eto', proto: '$proto', helo: '$helo'\n"; - $line =~ s/;$//; - - if ($line =~ /^(reject|hold|discard): (\S+) from ([^[]+)\[([^]]+)\](?::\d+)?: (.*)$/) { - - my ($action,$stage,$host,$hostip,$reply) = ($1,$2,$3,$4,$5); - #print "action: '$action', stage: '$stage', host: '$host', hostip: '$hostip', reply: '$reply'\n"; - - if ($action eq 'reject') { - my ($dsn,$fmthost,$reject_name); - ($dsn,$reply) = ($1,$2) if $reply =~ /^($re_DSN) (.*)$/o; - #print " dsn: '$dsn', reply: '$reply'\n"; - - if ($Collecting{'byiprejects'} and substr($dsn,0,1) eq '5') { - $fmthost = formathost($hostip,$host); - $Counts{'byiprejects'}{$fmthost}++; - } - # Note: reject_warning does not seem to occur - # Note: See rejectmilter elsewhere - $Totals{$reject_name = get_reject_key($dsn) . 'rejectmilter' }++; return unless ($Collecting{$reject_name}); - $Counts{$reject_name}{$stage}{$fmthost ? $fmthost : formathost($hostip,$host)}{$reply}++; - } - # milter-hold - elsif ($action eq 'hold') { - $Totals{'hold'}++; return unless ($Collecting{'hold'}); - $Counts{'hold'}{'milter'}{$stage}{formathost($hostip,$host)}{$eto}++; - } - # milter-discard - else { # $action eq 'discard' - $Totals{'discarded'}++; return unless ($Collecting{'discarded'}); - $Counts{'discarded'}{'milter'}{$stage}{formathost($hostip,$host)}{$eto}++; - } - - } - else { - inc_unmatched('milter_common)'); - } -} - -sub postfix_dnsblog { - my $line = shift; - - #postfix/dnsblog[16943]: addr 192.168.0.1 listed by domain bl.spamcop.net as 127.0.0.2 - #postfix/dnsblog[78598]: addr 192.168.0.1 blocked by domain zen.spamhaus.org as 127.0.0.11 - if ($line =~ /^addr (\S+) (?:listed|blocked) by domain (\S+) as (\S+)$/) { - $Counts{'dnsblog'}{$2}{$1}{$3}++ if $Collecting{'dnsblog'}; - } - else { - inc_unmatched('dnsblog') if ! in_ignore_list($line); - return; - } -} - -sub postfix_postscreen { - my $line = shift; - - return if ( - $line =~ /^cache / or - $line =~ /discarding EHLO keywords: / or - $line =~ /: discard_mask / or - $line =~ /: sq=\d+ cq=\d+ event/ or - $line =~ /: replacing command "/ - ); - - - if (($line =~ /^(PREGREET) \d+ (?:after \S+)? from \[([^]]+)\](?::\d+)?/) or - # PREGREET 20 after 0.31 from [192.168.0.1]:12345: HELO 10.0.0.1?? - # HANGUP after 0.7 from [192.168.0.4]:12345 - ($line =~ /^(HANGUP) (?:after \S+)? from \[([^]]+)\](?::\d+)?/)) { - $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++ if $Collecting{'postscreen'}; - } - elsif ($line =~ /^(WHITELISTED|BLACKLISTED|PASS \S+) \[([^]]+)\](?::\d+)?$/) { - # PASS NEW [192.168.0.2]:12345 - # PASS OLD [192.168.0.3]:12345 - $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++ if $Collecting{'postscreen'}; - } - elsif ($line =~ /^DNSBL rank (\S+) for \[([^]]+)\](?::\d+)?$/) { - $Counts{'postscreen'}{'dnsbl'}{$2}{$1}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^(CONNECT|COMMAND (?:(?:TIME|COUNT|LENGTH) LIMIT|PIPELINING)|NON-SMTP COMMAND|BARE NEWLINE) from \[([^\]]+)\]:\d+/) { - # CONNECT from [192.168.1.1]:12345 - $Counts{'postscreen'}{lc($1)}{$2}{$END_KEY}++ if $Collecting{'postscreen'}; - } - elsif ($line =~ /^DISCONNECT \[([^\]]+)\]:\d+$/) { - # DISCONNECT [192.168.1.1]:12345 - $Counts{'postscreen'}{'disconnect'}{$1}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^NOQUEUE: reject: RCPT from \[([^]]+)\](?::\d+)?: ($re_DSN) ([^;]+)/o) { - #NOQUEUE: reject: RCPT from [192.168.0.1]:12345: 550 5.7.1 Service unavailable; client [192.168.0.1] blocked using b.barracudacentral.org; from=<from@example.com>, to=<to@example.net>, proto=SMTP, helo=<example.com> - my ($ip,$dsn,$msg) = ($1,$2,$3); - - if ($dsn =~ /^([54])/) { - $Counts{'postscreen'}{$1 . 'xx reject'}{"$dsn $msg"}{$ip}++ if $Collecting{'postscreen'}; - } - else { - $Counts{'postscreen'}{'reject'}{"$dsn $msg"}{$ip}{$END_KEY}++ if $Collecting{'postscreen'}; - } - } - - elsif ($line =~ /^NOQUEUE: reject: CONNECT from \[([^]]+)\](?::\d+)?: too many connections/) { - # NOQUEUE: reject: CONNECT from [192.168.0.1]:7197: too many connections - $Counts{'postscreen'}{'reject'}{'Too many connections'}{$1}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^reject: connect from \[([^]]+)\](?::\d+)?: (.+)$/) { - # reject: connect from [192.168.0.1]:21225: all screening ports busy - $Counts{'postscreen'}{'reject'}{"\u$2"}{$1}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^(?:WHITELIST VETO) \[([^]]+)\](?::\d+)?$/) { - # WHITELIST VETO [192.168.0.8]:43579 - $Counts{'postscreen'}{'whitelist veto'}{$1}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^(entering|leaving) STRESS mode with (\d+) connections$/) { - # entering STRESS mode with 90 connections - $Counts{'postscreen'}{'stress mode: ' . $1}{$2}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - elsif ($line =~ /^close database (\S+): No such file or directory/) { - # close database /var/lib/postfix/postscreen_cache.db: No such file or directory (possible Berkeley DB bug) - $Counts{'postscreen'}{'close database'}{$1}{$END_KEY}++ if $Collecting{'postscreen'}; - } - - else { - inc_unmatched('postscreen') if ! in_ignore_list($line); - return; - } - - $Totals{'postscreen'}++; -} - - -# Handles postfix/postsuper lines -# -sub postfix_postsuper($) { - my $line = shift; - - return if $line =~ /^Deleted: \d+ messages?$/; - - if ($line =~ /^Placed on hold: (\d+) messages?$/o) { - #TDps Placed on hold: 2 messages - # Note: See Hold elsewhere - $Totals{'hold'} += $1; return unless ($Collecting{'hold'}); - $Counts{'hold'}{'Postsuper'}{'localhost'}{"bulk hold: $1"}{''} += $1; - } - elsif ($line =~ /^Released from hold: (\d+) messages?$/o) { - #TDps Released from hold: 1 message - $Totals{'releasedfromhold'} += $1; - } - elsif ($line =~ /^Requeued: (\d+) messages?$/o) { - #TDps Requeued: 1 message - $Totals{'requeued'} += $1; - } - elsif (my($qid,$p2) = ($line =~ /($re_QID): (.*)$/)) { - # postsuper double reports the following 3 lines - return if ($p2 eq 'released from hold'); - return if ($p2 eq 'placed on hold'); - return if ($p2 eq 'requeued'); - - if ($p2 =~ /^removed\s*$/o) { - # Note: See REMOVED elsewhere - # 52CBDC2E0F: removed - delete $SizeByQid{$qid} if (exists $SizeByQid{$qid}); - $Totals{'removedfromqueue'}++; - } - elsif (! in_ignore_list ($p2)) { - inc_unmatched('postsuper2'); - } - } - elsif (! in_ignore_list ($line)) { - inc_unmatched('postsuper1'); - } -} - -# Handles postfix panic: lines -# -sub postfix_panic($) { - #TD panic: myfree: corrupt or unallocated memory block - $Totals{'panicerror'}++; return unless ($Collecting{'panicerror'}); - $Counts{'panicerror'}{ucfirst($1)}++; -} - -# Handles postfix fatal: lines -# -sub postfix_fatal($) { - my ($reason) = shift; - - if ($reason =~ /^\S*\(\d+\): Message file too big$/o) { - #TD fatal: root(0): Message file too big - $Totals{'fatalfiletoobig'}++; - - - # XXX its not clear this is at all useful - consider falling through to last case - } elsif ( $reason =~ /^config variable (\S*): (.*)$/o ) { - #TD fatal: config variable inet_interfaces: host not found: 10.0.0.1:2525 - #TD fatal: config variable inet_interfaces: host not found: all:2525 - $Totals{'fatalconfigerror'}++; return unless ($Collecting{'fatalconfigerror'}); - $Counts{'fatalconfigerror'}{ucfirst($reason)}++; - } - else { - #TD fatal: watchdog timeout - #TD fatal: bad boolean configuration: smtpd_use_tls = - - #TDvN fatal: update queue file active/4B709F060E: File too large - $reason =~ s/(^update queue file \w+\/)\w+:/$1*:/; - - $Totals{'fatalerror'}++; return unless ($Collecting{'fatalerror'}); - $Counts{'fatalerror'}{ucfirst($reason)}++; - } -} -# Handles postfix fatal: lines -# -sub postfix_error($) { - my ($reason) = shift; - # postfix/postfix-script[4271]: error: unknown command: 'rel' - - $Totals{'error'}++; return unless ($Collecting{'fatalerror'}); - $Counts{'error'}{ucfirst($reason)}++; -} - -# Handles postfix warning: lines -# and additional lines coerced into warnings -# -sub postfix_warning($) { - my ($warning) = shift; - - # Skip these - return if ($warning =~ /$re_QID: skipping further client input$/o); - return if ($warning =~ /^Mail system is down -- accessing queue directly$/o); - return if ($warning =~ /^SASL authentication failure: (?:Password verification failed|no secret in database)$/o); - return if ($warning =~ /^no MX host for .* has a valid A record$/o); - return if ($warning =~ /^uid=\d+: Broken pipe$/o); - - #TD warning: connect to 127.0.0.1:12525: Connection refused - #TD warning: problem talking to server 127.0.0.1:12525: Connection refused - #TD warning: valid_ipv4_hostaddr: invalid octet count: - - my ($domain,$to,$type,$site,$helo,$cmd); - my ($addr,$size,$hostip,$host,$port,$reason,$qid,$queue,$reason2,$process,$status,$service); - - if (($hostip,$host,$reason) = ($warning =~ /^(?:smtpd_peer_init: )?([^:]+): hostname ([^ ]+) verification failed: (.*)$/) or - ($hostip,$reason,$host) = ($warning =~ /^(?:smtpd_peer_init: )?([^:]+): (address not listed for hostname) (.*)$/) or - ($host,$reason,$hostip,$reason2) = ($warning =~ /^(?:smtpd_peer_init: )?hostname (\S+) (does not resolve to address) ([\d.]+)(: host not found, try again)?$/)) { - #TD warning: 10.0.0.1: hostname sample.com verification failed: Host not found - #TD warning: smtpd_peer_init: 192.168.0.1: hostname example.com verification failed: Name or service not known - #TD warning: 192.168.0.1: address not listed for hostname sample.net - # post 2.8 - #TD warning: hostname 281.example.net does not resolve to address 192.168.0.1: host not found, try again - #TD warning: hostname 281.example.net does not resolve to address 192.168.0.1 - - $reason .= $reason2 if $reason2; - $Totals{'hostnameverification'}++; return unless ($Collecting{'hostnameverification'}); - $Counts{'hostnameverification'}{ucfirst($reason)}{formathost($hostip,$host)}++; - - } elsif (($warning =~ /^$re_QID: queue file size limit exceeded$/o) or - ($warning =~ /^uid=\d+: File too large$/o)) { - $Totals{'warnfiletoobig'}++; - - } elsif ($warning =~ /^database (?:[^ ]*) is older than source file ([\w\/]+)$/o) { - #TD warning: database /etc/postfix/client_checks.db is older than source file /etc/postfix/client_checks - $Totals{'databasegeneration'}++; return unless ($Collecting{'databasegeneration'}); - $Counts{'databasegeneration'}{$1}++; - - } elsif (($reason,$qid,$reason2) = ($warning =~ /^(open active) ($re_QID): (.*)$/o) or - ($reason,$qid,$reason2) = ($warning =~ /^qmgr_active_corrupt: (save corrupt file queue active) id ($re_QID): (.*)$/o) or - ($qid,$reason,$reason2) = ($warning =~ /^($re_QID): (write queue file): (.*)$/o)) { - - #TD warning: open active BDB9B1309F7: No such file or directory - #TD warning: qmgr_active_corrupt: save corrupt file queue active id 4F4272F342: No such file or directory - #TD warning: E669DE52: write queue file: No such file or directory - - $Totals{'queuewriteerror'}++; return unless ($Collecting{'queuewriteerror'}); - $Counts{'queuewriteerror'}{"$reason: $reason2"}{$qid}++; - - } elsif (($qid,$reason) = ($warning =~ /^qmgr_active_done_3_generic: remove ($re_QID) from active: (.*)$/o)) { - #TD warning: qmgr_active_done_3_generic: remove AF0F223FC05 from active: No such file or directory - $Totals{'queuewriteerror'}++; return unless ($Collecting{'queuewriteerror'}); - $Counts{'queuewriteerror'}{"remove from active: $reason"}{$qid}++; - - } elsif (($queue,$qid) = ($warning =~ /^([^\/]*)\/($re_QID): Error writing message file$/o )) { - #TD warning: maildrop/C9E66ADF: Error writing message file - $Totals{'messagewriteerror'}++; return unless ($Collecting{'messagewriteerror'}); - $Counts{'messagewriteerror'}{$queue}{$qid}++; - - } elsif (($process,$status) = ($warning =~ /^process ([^ ]*) pid \d+ exit status (\d+)$/o)) { - #TD warning: process /usr/lib/postfix/smtp pid 9724 exit status 1 - $Totals{'processexit'}++; return unless ($Collecting{'processexit'}); - $Counts{'processexit'}{"Exit status $status"}{$process}++; - - } elsif ($warning =~ /^mailer loop: (.*)$/o) { - #TD warning: mailer loop: best MX host for example.com is local - $Totals{'mailerloop'}++; return unless ($Collecting{'mailerloop'}); - $Counts{'mailerloop'}{$1}++; - - } elsif ($warning =~ /^no (\S+) host for (\S+) has a valid address record$/) { - #TDs warning: no MX host for example.com has a valid address record - $Totals{'dnserror'}++; return unless ($Collecting{'dnserror'}); - $Counts{'dnserror'}{"No $1 host has a valid address record"}{$2}{$END_KEY}++; - - } elsif ($warning =~ /^(Unable to look up \S+ host) (.+)$/) { - #TDsd warning: Unable to look up MX host for example.com: Host not found - #TDsd warning: Unable to look up MX host mail.example.com for Sender address from@example.com: hostname nor servname provided, or not known - #TDsd warning: Unable to look up NS host ns1.example.logal for Sender address bounce@example.local: No address associated with hostname - $Totals{'dnserror'}++; return unless ($Collecting{'dnserror'}); - - my ($problem,$target,$reason) = ($1, split(/: /,$2)); - $reason =~ s/, try again//; - - if ($target =~ /^for (\S+)$/) { - $Counts{'dnserror'}{$problem}{ucfirst($reason)}{$1}{$END_KEY}++; - } - elsif ($target =~ /^(\S+)( for \S+ address) (\S+)$/) { - $Counts{'dnserror'}{$problem . lc($2)}{ucfirst($reason)}{$1}{$3}++; - } - - } elsif ($warning =~ /^((?:malformed|numeric) domain name in .+? of \S+ record) for (.*):(.*)?$/) { - my ($problem,$domain,$reason) = ($1,$2,$3); - #TDsd warning: malformed domain name in resource data of MX record for example.com: - #TDsd warning: malformed domain name in resource data of MX record for example.com: mail.example.com\\032 - #TDsd warning: numeric domain name in resource data of MX record for sample.com: 192.168.0.1 - $Totals{'dnserror'}++; return unless ($Collecting{'dnserror'}); - $Counts{'dnserror'}{ucfirst($problem)}{$domain}{$reason eq '' ? '*unknown' : $reason}{$END_KEY}++; - - } elsif ($warning =~ /^numeric hostname: ([\S]+)$/) { - #TD warning: numeric hostname: 192.168.0.1 - $Totals{'numerichostname'}++; return unless ($Collecting{'numerichostname'}); - $Counts{'numerichostname'}{$1}++; - - } elsif ( ($host,$hostip,$port,$type,$reason) = ($warning =~ /^([^[]+)\[([^]]+)\](?::(\d+))? (sent \w+ header instead of SMTP command): (.*)$/) or - ($type,$host,$hostip,$port,$reason) = ($warning =~ /^(non-E?SMTP command) from ([^[]+)\[([^]]+)\](?::(\d+))?: (.*)$/) or - ($type,$host,$hostip,$port,$reason) = ($warning =~ /^(?:$re_QID: )?(non-E?SMTP response) from ([^[]+)\[([^]]+)\](?::(\d+))?:(?: (.*))?$/o)) { - # ancient - #TDsd warning: example.com[192.168.0.1] sent message header instead of SMTP command: From: "Someone" <40245426501example.com> - # current - #TDsd warning: non-SMTP command from sample.net[10.0.0.1]: Received: from 192.168.0.1 (HELO bogus.sample.com) - #TDs warning: 6B01A8DEF: non-ESMTP response from mail.example.com[192.168.0.1]:25: - - $Totals{'smtpconversationerror'}++; return unless ($Collecting{'smtpconversationerror'}); - $host .= ' :' . $port if ($port and $port ne '25'); - $Counts{'smtpconversationerror'}{ucfirst($type)}{formathost($hostip,$host)}{$reason}++; - - } elsif ($warning =~ /^valid_hostname: (.*)$/o) { - #TD warning: valid_hostname: empty hostname - $Totals{'hostnamevalidationerror'}++; return unless ($Collecting{'hostnamevalidationerror'}); - $Counts{'hostnamevalidationerror'}{$1}++; - - } elsif (($host,$hostip,$type,$reason) = ($warning =~ /^([^[]+)\[([^]]+)\](?::\d+)?: SASL (.*) authentication failed(.*)$/)) { - #TDsd warning: unknown[10.0.0.1]: SASL LOGIN authentication failed: bad protocol / cancel - #TDsd warning: example.com[192.168.0.1]: SASL DIGEST-MD5 authentication failed - # see saslauthfail elsewhere - $Totals{'saslauthfail'}++; return unless ($Collecting{'saslauthfail'}); - if ($reason) { $reason = $type . $reason; } - else { $reason = $type; } - $Counts{'saslauthfail'}{$reason}{formathost($hostip,$host)}++; - - } elsif (($host,$reason) = ($warning =~ /^(\S+): RBL lookup error:.* Name service error for (?:name=)?\1(?: type=[^:]+)?: (.*)$/o)) { - #TD warning: 192.168.0.1.sbl.spamhaus.org: RBL lookup error: Host or domain name not found. Name service error for name=192.168.0.1.sbl.spamhaus.org type=A: Host not found, try again - - #TD warning: 10.0.0.1.relays.osirusoft.com: RBL lookup error: Name service error for 10.0.0.1.relays.osirusoft.com: Host not found, try again - $Totals{'rblerror'}++; return unless ($Collecting{'rblerror'}); - $Counts{'rblerror'}{$reason}{$host}++; - - } elsif ( - ($host,$hostip,$reason,$helo) = ($warning =~ /^host ([^[]+)\[([^]]+)\](?::\d+)? (greeted me with my own hostname) ([^ ]*)$/ ) or - ($host,$hostip,$reason,$helo) = ($warning =~ /^host ([^[]+)\[([^]]+)\](?::\d+)? (replied to HELO\/EHLO with my own hostname) ([^ ]*)$/ )) { - #TDs warning: host example.com[192.168.0.1] greeted me with my own hostname example.com - #TDs warning: host example.com[192.168.0.1] replied to HELO/EHLO with my own hostname example.com - $Totals{'heloerror'}++; return unless ($Collecting{'heloerror'}); - $Counts{'heloerror'}{ucfirst($reason)}{formathost($hostip,$host)}++; - - } elsif (($size,$host,$hostip) = ($warning =~ /^bad size limit "([^"]+)" in EHLO reply from ([^[]+)\[([^]]+)\](?::\d+)?$/ )) { - #TD warning: bad size limit "-679215104" in EHLO reply from example.com[192.168.0.1] - $Totals{'heloerror'}++; return unless ($Collecting{'heloerror'}); - $Counts{'heloerror'}{"Bad size limit in EHLO reply"}{formathost($hostip,$host)}{"$size"}++; - - } elsif ( ($host,$hostip,$cmd,$addr) = ($warning =~ /^Illegal address syntax from ([^[]+)\[([^]]+)\](?::\d+)? in ([^ ]*) command: (.*)/ )) { - #TD warning: Illegal address syntax from example.com[192.168.0.1] in MAIL command: user@sample.net - $addr =~ s/[<>]//g unless ($addr eq '<>'); - $Totals{'illegaladdrsyntax'}++; return unless ($Collecting{'illegaladdrsyntax'}); - $Counts{'illegaladdrsyntax'}{$cmd}{$addr}{formathost($hostip,$host)}++; - - } elsif ($warning =~ /^(timeout|premature end-of-input) on (.+) while reading (.*)$/o - or $warning =~ /^(malformed (?:base64|numerical)|unexpected end-of-input) from (.+) while reading (.*)$/o) { - - #TDs warning: premature end-of-input on private/anvil while reading input attribute name - #TDs warning: timeout on private/anvil while reading input attribute data - #TDs warning: unexpected end-of-input from 127.0.0.1:10025 socket while reading input attribute name - #TDs warning: malformed base64 data from %s while reading input attribute data: ... - #TDs warning: malformed numerical data from %s while reading input attribute data: ... - - $Totals{'attrerror'}++; return unless ($Collecting{'attrerror'}); - $Counts{'attrerror'}{$2}{$1}{$3}++; - - } elsif ($warning =~ /^(.*): (bad command startup -- throttling)/o) { - #TD warning: /usr/libexec/postfix/trivial-rewrite: bad command startup -- throttling - $Totals{'startuperror'}++; return unless ($Collecting{'startuperror'}); - $Counts{'startuperror'}{ucfirst($2)}{$1}++; - - } elsif ($warning =~ /(problem talking to service [^:]*): (.*)$/o) { - #TD warning: problem talking to service rewrite: Connection reset by peer - #TD warning: problem talking to service rewrite: Success - $Totals{'communicationerror'}++; return unless ($Collecting{'communicationerror'}); - $Counts{'communicationerror'}{ucfirst($1)}{$2}++; - - } elsif (my ($map,$key) = ($warning =~ /^$re_QID: ([^ ]*) map lookup problem for (.*)$/o)) { - #TD warning: 6F74F74431: virtual_alias_maps map lookup problem for root@example.com - $Totals{'mapproblem'}++; return unless ($Collecting{'mapproblem'}); - $Counts{'mapproblem'}{$map}{$key}++; - - } elsif (($map,$reason) = ($warning =~ /^pcre map ([^,]+), (.*)$/o)) { - #TD warning: pcre map /etc/postfix/body_checks, line 92: unknown regexp option "F": skipping this rule - $Totals{'mapproblem'}++; return unless ($Collecting{'mapproblem'}); - $Counts{'mapproblem'}{$map}{$reason}++; - - } elsif (($reason) = ($warning =~ /dict_ldap_lookup: (.*)$/o)) { - #TD warning: dict_ldap_lookup: Search error 80: Internal (implementation specific) error - $Totals{'ldaperror'}++; return unless ($Collecting{'ldaperror'}); - $Counts{'ldaperror'}{$reason}++; - - } elsif (($type,$size,$host,$hostip,$service) = ($warning =~ /^(.+) limit exceeded: (\d+) from ([^[]+)\[([^]]+)\](?::\d+)? for service (.*)/ )) { - #TDsd warning: Connection concurrency limit exceeded: 51 from example.com[192.168.0.1] for service smtp - #TDsd warning: Connection rate limit exceeded: 20 from mail.example.com[192.168.0.1] for service smtp - #TDsd warning: Connection rate limit exceeded: 30 from unknown[unknown] for service smtp - #TDsd warning: Recipient address rate limit exceeded: 21 from example.com[10.0.0.1] for service smtp - #TDsd warning: Message delivery request rate limit exceeded: 11 from example.com[10.0.0.1] for service smtp - #TDsd warning: New TLS session rate limit exceeded: 49 from example.com[10.0.0.1] for service smtp - $Totals{'anvil'}++; return unless ($Collecting{'anvil'}); - $Counts{'anvil'}{$service}{$type}{formathost($hostip,$host)}{$size}++; - - } elsif (my ($extname,$intname,$limit) = ($warning =~ /service "([^"]+)" \(([^)]+)\) has reached its process limit "([^"]+)":/o)) { - #TD warning: service "smtp" (25) has reached its process limit "50": new clients may experience noticeable delays - $Totals{'processlimit'}++; return unless ($Collecting{'processlimit'}); - $Counts{'processlimit'}{'See http://www.postfix.org/STRESS_README.html'}{"$extname ($intname)"}{$limit}++; - - } else { - #TDsd warning: No server certs available. TLS won't be enabled - #TDs warning: smtp_connect_addr: bind <localip>: Address already in use - - # These two messages follow ProcessLimit message above - #TDm warning: to avoid this condition, increase the process count in master.cf or reduce the service time per client - #TDm warning: see http://www.postfix.org/STRESS_README.html for examples of stress-dependent configuration settings - return if ($warning =~ /^to avoid this condition,/o); - return if ($warning =~ /^see http:\/\/www\.postfix\.org\/STRESS_README.html/o); - - #TDsd warning: 009314BD9E: read timeout on cleanup socket - $warning =~ s/^$re_QID: (read timeout on \S+ socket)/$1/; - - #TDsd warning: Read failed in network_biopair_interop with errno=0: num_read=0, want_read=11 - #TDs warning: Read failed in network_biopair_interop with errno=0: num_read=0, want_read=11 - $warning =~ s/^(Read failed in network_biopair_interop) with .*$/$1/; - - $Totals{'warningsother'}++; return unless ($Collecting{'warningsother'}); - $Counts{'warningsother'}{$warning}++; - } -} - -# Handles postfix/postfix-script lines -# -sub postfix_script($) { - my $line = shift; - - return if ($line =~ /^the Postfix mail system is running: PID: /o); - - if ($line =~ /^starting the Postfix mail system/o) { - $Totals{'postfixstart'}++; - } - elsif ($line =~ /^stopping the Postfix mail system/o) { - $Totals{'postfixstop'}++; - } - elsif ($line =~ /^refreshing the Postfix mail system/o) { - $Totals{'postfixrefresh'}++; - } - elsif ($line =~ /^waiting for the Postfix mail system to terminate/o) { - $Totals{'postfixwaiting'}++; - } - elsif (! in_ignore_list ($line)) { - inc_unmatched('postfix_script'); - } -} - -# Clean up a server's reply, to give some uniformity to reports -# -sub cleanhostreply($ $ $ $) { - my ($hostreply,$relay,$recip,$domain) = @_; - - my $fmtdhost = ''; - my ($r1, $r2, $dsn, $msg, $host, $event); - - #print "RELAY: $relay, RECIP: $recip, DOMAIN: $domain\n"; - #print "HOSTREPLY: \"$hostreply\"\n"; - return ('Accepted', '*unknown') if $hostreply =~ /^25\d/o; - - # Host or domain name not found. Name service error for name=example.com type=MX: Host not found... - if ($hostreply =~ /^Host or domain name not found. Name service error for name=([^:]+): Host not found/o) { - return ('Host not found', $1); - } - - if (($host,$dsn,$r1) = ($hostreply =~ /host (\S+) said: ($re_DSN)[\- :]*"?(.*)"?$/o)) { - # Strip recipient address from host's reply - we already have it in $recip. - $r1 =~ s/[<(]?\Q$recip\E[>)]?\W*//ig; - - # Strip and capture "in reply to XYZ command" from host's reply - if ($r1 =~ s/\s*[(]?(?:in reply to (.*) command)[)]?//o) { - $r2 = ": $1"; - } - $r1 =~ s/^Recipient address rejected: //o; - # Canonicalize numerous forms of "recipient unknown" - if ( $r1 =~ /^user unknown/i - or $r1 =~ /^unknown user/i - or $r1 =~ /^unknown recipient address/i - or $r1 =~ /^invalid recipient/i - or $r1 =~ /^recipient unknown/i - or $r1 =~ /^sorry, no mailbox here by that name/i - or $r1 =~ /^User is unknown/ - or $r1 =~ /^User not known/ - or $r1 =~ /^MAILBOX NOT FOUND/ - or $r1 =~ /^Recipient Rejected: No account by that name here/ - or $r1 =~ /^Recipient does not exist here/ - or $r1 =~ /The email account that you tried to reach does not exist./ # Google's long mess - or $r1 =~ /(?:no such user|user unknown)/i - ) - { - #print "UNKNOWN RECIP: $r1\n"; - $r1 = 'Unknown recipient'; - } - elsif ($r1 =~ /greylisted/oi) { - #print "GREYLISTED RECIP: $r1\n"; - $r1 = 'Recipient greylisted'; - } - elsif ($r1 =~ /^Message temporarily deferred - (\d\.\d+\.\d+)\. Please refer to (.+)$/o) { - # Yahoo: 421 Message temporarily deferred - 4.16.51. Please refer to http://... (in reply to end of DATA command)) - $dsn = "$dsn $1"; $r1 = "see $2"; - } - elsif ($r1 =~ /^Resources temporarily not available - Please try again later \[#(\d\.\d+\.\d+)\]\.$/o) { - #Yahoo 451 Resources temporarily not available - Please try again later [#4.16.5]. - $dsn = "$dsn $1"; $r1 = "resources not available"; - } - elsif ($r1 =~ /^Message temporarily deferred - (\[\d+\])/o) { - # Yahoo: 451 Message temporarily deferred - [160] - $dsn = "$dsn $1"; $r1 = ''; - } - } - - elsif ($hostreply =~ /^connect to (\S+): (.*)$/o) { - #print "CONNECT: $hostreply\n"; - $host = $1; $r1 = $2; $r1 =~ s/server refused to talk to me/refused/; - } - - elsif ($hostreply =~ /^host (\S+) refused to talk to me: (.*)$/o) { - $host = $1; $msg = $2; - #print "HOSTREFUSED: $hostreply\n"; - #Yahoo: '421 Message from (10.0.0.1) temporarily deferred - 4.16.50. Please refer to http://... - if ($msg =~ /^(\d+) Message from \([^)]+\) temporarily deferred - (\d\.\d+\.\d+)\. Please refer to (.+)$/) { - $dsn = "$1 $2"; $msg = "see $3"; - } - #$r1 = join(': ', 'refused', $msg); - $r1 = $msg; - } - elsif ($hostreply =~ /^(delivery temporarily suspended): connect to (\S+): (.*)$/o) { - #print "DELIVERY SUSP: $hostreply\n"; - $host = $2; $r1 = join(': ', $1, $3); - } - elsif ($hostreply =~ /^(delivery temporarily suspended: conversation) with (\S+) (.*)$/o) { - # delivery temporarily suspended: conversation with example.com[10.0.0.1] timed out while receiving the initial server greeting) - #print "DELIVERY SUSP2: $hostreply\n"; - $host = $2; $r1 = join(' ', $1, $3); - } - elsif (($event,$host,$r1) = ($hostreply =~ /^(lost connection|conversation) with (\S+) (.*)$/o)) { - #print "LOST conv/conn: $hostreply\n"; - $r1 = join(' ',$event,$r1); - } - elsif ($hostreply =~ /^(.*: \S+maildrop: Unable to create a dot-lock) at .*$/o) { - #print "MAILDROP: $hostreply\n"; - $r1 = $1; - } - elsif ($hostreply =~ /^mail for (\S+) loops back to myself/o) { - #print "LOOP: $hostreply\n"; - $host = $1; $r1 = 'mailer loop'; - } - elsif ($hostreply =~ /^unable to find primary relay for (\S+)$/o) { - #print "NORELAY: $hostreply\n"; - $host = $1; $r1 = 'no relay found'; - } - elsif ($hostreply =~ /^message size \d+ exceeds size limit \d+ of server (\S+)\s*$/o) { - #print "TOOBIG: $hostreply\n"; - $host = $1; $r1 = 'message too big'; - } - else { - #print "UNMATCH: $hostreply\n"; - $r1 = $hostreply; - } - - #print "R1: $r1, R2: $r2\n"; - $r1 =~ s/for name=\Q$domain\E //ig; - - if ($host eq '') { - if ($relay =~ /([^[]+)\[([^]]+)\]/) { - $fmtdhost = formathost($2,$1); - } - else { - $fmtdhost = '*unknown'; - } - } - elsif ($host =~ /^([^[]+)\[([^]]+)\]/) { - $fmtdhost = formathost($2,$1); - } - else { - $fmtdhost = $host; - } - - return (($dsn ? "$dsn " : '' ) . "\u$r1$r2", $fmtdhost); -} - -# Strip and return from, to, proto, and helo information from a log line -# From is set to the empty envelope sender <> as necessary, and To is -# always lowercased. -# -# Note: modifies its input for efficiency -# -sub strip_ftph($) { - my ($helo, $proto, $to, $from); - #print "strip_ftph: '$_[0]\n"; - $helo = ($_[0] =~ s/\s+helo=<(.*?)>\s*$//) == 1 ? $1 : '*unavailable'; - $proto = ($_[0] =~ s/\s+proto=(\S+)\s*$//) == 1 ? $1 : '*unavailable'; - $to = ($_[0] =~ s/\s+to=<(.*?)>\s*$//) == 1 ? (lc($1) || '<>') : '*unavailable'; - $from = ($_[0] =~ s/\s+from=<(.*?)>\s*$//) == 1 ? ( $1 || '<>') : '*unavailable'; - - #print "helo: $helo, proto: $proto, to: $to, from: $from\n"; - #print "strip_ftph: final: '$_[0]'\n"; - return ($from,$to,$proto,$helo); -} - -# Initialize the Getopts option list. Requires the Section table to -# be built already. -# -sub init_getopts_table() { - print "init_getopts_table: enter\n" if $Opts{'debug'} & Logreporters::D_ARGS; - - init_getopts_table_common(@supplemental_reports); - - add_option ('recipient_delimiter=s'); - add_option ('delays!'); - add_option ('show_delays=i', sub { $Opts{'delays'} = $_[1]; 1; }); - add_option ('delays_percentiles=s'); - add_option ('reject_reply_patterns=s'); - add_option ('ignore_services=s'); - add_option ('postgrey_delays!'); - add_option ('postgrey_show_delays=i', sub { $Opts{'postgrey_delays'} = $_[1]; 1; }); - add_option ('postgrey_delays_percentiles=s'); - add_option ('unknown!', sub { $Opts{'unknown'} = $_[1]; 1; }); - add_option ('show_unknown=i', sub { $Opts{'unknown'} = $_[1]; 1; }); - add_option ('enable_long_queue_ids=i', sub { $Opts{'long_queue_ids'} = $_[1]; 1; }); - add_option ('long_queue_ids!'); - -=pod - # aliases and backwards compatibility - add_option ('msgsdeferred=s', \$Opts{'deferred'}); - add_option ('msgsdelivered=s', \$Opts{'delivered'}); - add_option ('msgssent=s', \$Opts{'sent'}); - add_option ('msgssentlmtp=s', \$Opts{'sentlmtp'}); - add_option ('msgsforwarded=s', \$Opts{'forwarded'}); - add_option ('msgsresent=s', \$Opts{'resent'}); - add_option ('warn=s', \$Opts{'warned'}); - add_option ('held=s', \$Opts{'hold'}); -=cut -} - -# Builds the entire @Section table used for data collection -# -# Each Section entry has as many as six fields: -# -# 1. Section array reference -# 2. Key to %Counts, %Totals accumulator hashes, and %Collecting hash -# 3. Output in Detail report? (must also a %Counts accumulator) -# 4. Numeric output format specifier for Summary report -# 5. Section title for Summary and Detail reports -# 6. A hash to a divisor used to calculate the percentage of a total for that key -# -# Use begin_section_group/end_section_group to create groupings around sections. -# -# Sections can be freely reordered if desired, but maintain proper group nesting. -# -# -# The reject* entries of this table are dynamic, in that they are built based -# upon the value of $Opts{'reject_reply_patterns'}, which can be specified by -# either command line or configuration file. This allows various flavors, of -# reject sections based on SMTP reply code (eg. 421 45x, 5xx, etc.). Instead -# of creating special sections for each reject variant, the primary key of each -# reject section could have been the SMTP reply code. However, this would -# require special-case processing to distinguish 4xx temporary rejects from 5xx -# permanent rejects in various Totals{'totalrejects*'} counts, and in the -# Totals{'totalrejects'} tally. -# -# Sections can be freely reordered if desired. -sub build_sect_table() { - if ($Opts{'debug'} & Logreporters::D_SECT) { - print "build_sect_table: enter\n"; - print "\treject patterns: $Opts{'reject_reply_patterns'}\n"; - } - my $S = \@Sections; - - # References to these are used in the Sections table below; we'll predeclare them. - $Totals{'totalrejects'} = 0; - $Totals{'totalrejectswarn'} = 0; - $Totals{'totalacceptplusreject'} = 0; - - # Configuration and critical errors appear first - - # SECTIONREF, NAME, DETAIL, FMT, TITLE, DIVISOR - begin_section_group ($S, 'warnings'); - add_section ($S, 'panicerror', 1, 'd', '*Panic: General panic'); - add_section ($S, 'fatalfiletoobig', 0, 'd', '*Fatal: Message file too big'); - add_section ($S, 'fatalconfigerror', 1, 'd', '*Fatal: Configuration error'); - add_section ($S, 'fatalerror', 1, 'd', '*Fatal: General fatal'); - add_section ($S, 'error', 1, 'd', '*Error: General error'); - add_section ($S, 'processlimit', 1, 'd', '*Warning: Process limit reached, clients may delay'); - add_section ($S, 'warnfiletoobig', 0, 'd', '*Warning: Queue file size limit exceeded'); - add_section ($S, 'warninsufficientspace', 0, 'd', '*Warning: Insufficient system storage error'); - add_section ($S, 'warnconfigerror', 1, 'd', '*Warning: Server configuration error'); - add_section ($S, 'queuewriteerror', 1, 'd', '*Warning: Error writing queue file'); - add_section ($S, 'messagewriteerror', 1, 'd', '*Warning: Error writing message file'); - add_section ($S, 'databasegeneration', 1, 'd', '*Warning: Database is older than source file'); - add_section ($S, 'mailerloop', 1, 'd', '*Warning: Mailer loop'); - add_section ($S, 'startuperror', 1, 'd', '*Warning: Startup error'); - add_section ($S, 'mapproblem', 1, 'd', '*Warning: Map lookup problem'); - add_section ($S, 'attrerror', 1, 'd', '*Warning: Error reading attribute data'); - add_section ($S, 'anvil', 1, 'd', '*Warning: Anvil limit reached'); - add_section ($S, 'processexit', 1, 'd', 'Process exited'); - add_section ($S, 'hold', 1, 'd', 'Placed on hold'); - add_section ($S, 'communicationerror', 1, 'd', 'Postfix communications error'); - add_section ($S, 'saslauthfail', 1, 'd', 'SASL authentication failed'); - add_section ($S, 'ldaperror', 1, 'd', 'LDAP error'); - add_section ($S, 'warningsother', 1, 'd', 'Miscellaneous warnings'); - add_section ($S, 'totalrejectswarn', 0, 'd', 'Reject warnings (warn_if_reject)'); - end_section_group ($S, 'warnings'); - - begin_section_group ($S, 'bytes', "\n"); - add_section ($S, 'bytesaccepted', 0, 'Z', 'Bytes accepted '); # Z means print scaled as in 1k, 1m, etc. - add_section ($S, 'bytessentsmtp', 0, 'Z', 'Bytes sent via SMTP'); - add_section ($S, 'bytessentlmtp', 0, 'Z', 'Bytes sent via LMTP'); - add_section ($S, 'bytesdelivered', 0, 'Z', 'Bytes delivered'); - add_section ($S, 'bytesforwarded', 0, 'Z', 'Bytes forwarded'); - end_section_group ($S, 'bytes', $sep1); - - begin_section_group ($S, 'acceptreject', "\n"); - begin_section_group ($S, 'acceptreject2', "\n"); - add_section ($S, 'msgsaccepted', 0, 'd', 'Accepted', \$Totals{'totalacceptplusreject'}); - add_section ($S, 'totalrejects', 0, 'd', 'Rejected', \$Totals{'totalacceptplusreject'}); - end_section_group ($S, 'acceptreject2', $sep2); - add_section ($S, 'totalacceptplusreject', 0, 'd', 'Total', \$Totals{'totalacceptplusreject'}); - end_section_group ($S, 'acceptreject', $sep1); - - # The various Reject sections are built dynamically based upon a list of reject reply keys, - # which are user-configured via $Opts{'reject_reply_patterns'} - @RejectPats = (); - foreach my $rejpat (split /[ ,]/, $Opts{'reject_reply_patterns'}) { - if ($rejpat !~ /^(warn|[45][\d.]{2})$/io) { - print STDERR usage "Invalid pattern \"$rejpat\" in reject_reply_patterns"; - exit (2); - } - if (grep (/\Q$rejpat\E/, @RejectPats) == 0) { - push @RejectPats, $rejpat - } - else { - print STDERR "Ignoring duplicate pattern \"$rejpat\" in reject_reply_patterns\n"; - } - } - @RejectKeys = @RejectPats; - for (@RejectKeys) { - s/\./x/g; - } - - print "\tRejectPat: \"@RejectPats\", RejectKeys: \"@RejectKeys\"\n" if $Opts{'debug'} & Logreporters::D_SECT; - - # Add reject variants - foreach my $key (@RejectKeys) { - $key = lc($key); - my $keyuc = ucfirst($key); - my $totalsref = \$Totals{'totalrejects' . $key}; - print "\t reject key: $key\n" if $Opts{'debug'} & Logreporters::D_SECT; - - begin_section_group ($S, 'rejects', "\n"); - begin_section_group ($S, 'rejects2', "\n"); - add_section ($S, $key . 'rejectrelay', 1, 'd', $keyuc . ' Reject relay denied', $totalsref); - add_section ($S, $key . 'rejecthelo', 1, 'd', $keyuc . ' Reject HELO/EHLO', $totalsref); - add_section ($S, $key . 'rejectdata', 1, 'd', $keyuc . ' Reject DATA', $totalsref); - add_section ($S, $key . 'rejectunknownuser', 1, 'd', $keyuc . ' Reject unknown user', $totalsref); - add_section ($S, $key . 'rejectrecip', 1, 'd', $keyuc . ' Reject recipient address', $totalsref); - add_section ($S, $key . 'rejectsender', 1, 'd', $keyuc . ' Reject sender address', $totalsref); - add_section ($S, $key . 'rejectclient', 1, 'd', $keyuc . ' Reject client host', $totalsref); - add_section ($S, $key . 'rejectunknownclient', 1, 'd', $keyuc . ' Reject unknown client host', $totalsref); - add_section ($S, $key . 'rejectunknownreverseclient', 1, 'd', $keyuc . ' Reject unknown reverse client host', $totalsref); - add_section ($S, $key . 'rejectunverifiedclient', 1, 'd', $keyuc . ' Reject unverified client host', $totalsref); - add_section ($S, $key . 'rejectrbl', 1, 'd', $keyuc . ' Reject RBL', $totalsref); - add_section ($S, $key . 'rejectheader', 1, 'd', $keyuc . ' Reject header', $totalsref); - add_section ($S, $key . 'rejectbody', 1, 'd', $keyuc . ' Reject body', $totalsref); - add_section ($S, $key . 'rejectcontent', 1, 'd', $keyuc . ' Reject content', $totalsref); - add_section ($S, $key . 'rejectsize', 1, 'd', $keyuc . ' Reject message size', $totalsref); - add_section ($S, $key . 'rejectmilter', 1, 'd', $keyuc . ' Reject milter', $totalsref); - add_section ($S, $key . 'rejectproxy', 1, 'd', $keyuc . ' Reject proxy', $totalsref); - add_section ($S, $key . 'rejectinsufficientspace', 1, 'd', $keyuc . ' Reject insufficient space', $totalsref); - add_section ($S, $key . 'rejectconfigerror', 1, 'd', $keyuc . ' Reject server config error', $totalsref); - add_section ($S, $key . 'rejectverify', 1, 'd', $keyuc . ' Reject VRFY', $totalsref); - add_section ($S, $key . 'rejectetrn', 1, 'd', $keyuc . ' Reject ETRN', $totalsref); - add_section ($S, $key . 'rejectlookupfailure', 1, 'd', $keyuc . ' Reject temporary lookup failure', $totalsref); - end_section_group ($S, 'rejects2', $sep2); - add_section ($S, 'totalrejects' . $key, 0, 'd', "Total $keyuc Rejects", $totalsref); - end_section_group ($S, 'rejects', $sep1); - - $Totals{'totalrejects' . $key} = 0; - } - - begin_section_group ($S, 'byiprejects', "\n"); - add_section ($S, 'byiprejects', 1, 'd', 'Reject by IP'); - end_section_group ($S, 'byiprejects'); - - begin_section_group ($S, 'general1', "\n"); - add_section ($S, 'connectioninbound', 1, 'd', 'Connections'); - add_section ($S, 'connectionlostinbound', 1, 'd', 'Connections lost (inbound)'); - add_section ($S, 'connectionlostoutbound', 1, 'd', 'Connections lost (outbound)'); - add_section ($S, 'disconnection', 0, 'd', 'Disconnections'); - add_section ($S, 'removedfromqueue', 0, 'd', 'Removed from queue'); - add_section ($S, 'delivered', 1, 'd', 'Delivered'); - add_section ($S, 'sent', 1, 'd', 'Sent via SMTP'); - add_section ($S, 'sentlmtp', 1, 'd', 'Sent via LMTP'); - add_section ($S, 'forwarded', 1, 'd', 'Forwarded'); - add_section ($S, 'resent', 0, 'd', 'Resent'); - add_section ($S, 'deferred', 1, 'd', 'Deferred'); - add_section ($S, 'deferrals', 1, 'd', 'Deferrals'); - add_section ($S, 'bouncelocal', 1, 'd', 'Bounced (local)'); - add_section ($S, 'bounceremote', 1, 'd', 'Bounced (remote)'); - add_section ($S, 'bouncefailed', 1, 'd', 'Bounce failure'); - add_section ($S, 'postscreen', 1, 'd', 'Postscreen'); - add_section ($S, 'dnsblog', 1, 'd', 'DNSBL log'); - - add_section ($S, 'envelopesenders', 1, 'd', 'Envelope senders'); - add_section ($S, 'envelopesenderdomains', 1, 'd', 'Envelope sender domains'); - - add_section ($S, 'bcced', 1, 'd', 'BCCed'); - add_section ($S, 'filtered', 1, 'd', 'Filtered'); - add_section ($S, 'redirected', 1, 'd', 'Redirected'); - add_section ($S, 'discarded', 1, 'd', 'Discarded'); - add_section ($S, 'prepended', 1, 'd', 'Prepended'); - add_section ($S, 'replaced', 1, 'd', 'Replaced'); - add_section ($S, 'warned', 1, 'd', 'Warned'); - - add_section ($S, 'requeued', 0, 'd', 'Requeued messages'); - add_section ($S, 'returnedtosender', 1, 'd', 'Expired and returned to sender'); - add_section ($S, 'notificationsent', 1, 'd', 'Notifications sent'); - - add_section ($S, 'policyspf', 1, 'd', 'Policy SPF'); - add_section ($S, 'policydweight', 1, 'd', 'Policyd-weight'); - add_section ($S, 'postfwd', 1, 'd', 'Postfwd'); - add_section ($S, 'postgrey', 1, 'd', 'Postgrey'); - end_section_group ($S, 'general1'); - - begin_section_group ($S, 'general2', "\n"); - add_section ($S, 'connecttofailure', 1, 'd', 'Connection failures (outbound)'); - add_section ($S, 'timeoutinbound', 1, 'd', 'Timeouts (inbound)'); - add_section ($S, 'heloerror', 1, 'd', 'HELO/EHLO conversations errors'); - add_section ($S, 'illegaladdrsyntax', 1, 'd', 'Illegal address syntax in SMTP command'); - add_section ($S, 'released', 0, 'd', 'Released from hold'); - add_section ($S, 'rblerror', 1, 'd', 'RBL lookup errors'); - add_section ($S, 'dnserror', 1, 'd', 'DNS lookup errors'); - add_section ($S, 'numerichostname', 1, 'd', 'Numeric hostname'); - add_section ($S, 'smtpconversationerror', 1, 'd', 'SMTP dialog errors'); - add_section ($S, 'hostnameverification', 1, 'd', 'Hostname verification errors (FCRDNS)'); - add_section ($S, 'hostnamevalidationerror', 1, 'd', 'Hostname validation errors'); - add_section ($S, 'smtpprotocolviolation', 1, 'd', 'SMTP protocol violations'); - add_section ($S, 'deliverable', 1, 'd', 'Deliverable (address verification)'); - add_section ($S, 'undeliverable', 1, 'd', 'Undeliverable (address verification)'); - add_section ($S, 'tablechanged', 0, 'd', 'Restarts due to lookup table change'); - add_section ($S, 'pixworkaround', 1, 'd', 'PIX workaround enabled'); - add_section ($S, 'tlsserverconnect', 1, 'd', 'TLS connections (server)'); - add_section ($S, 'tlsclientconnect', 1, 'd', 'TLS connections (client)'); - add_section ($S, 'saslauth', 1, 'd', 'SASL authenticated messages'); - add_section ($S, 'tlsunverified', 1, 'd', 'TLS certificate unverified'); - add_section ($S, 'tlsoffered', 1, 'd', 'Host offered TLS'); - end_section_group ($S, 'general2'); - - begin_section_group ($S, 'postfixstate', "\n"); - add_section ($S, 'postfixstart', 0, 'd', 'Postfix start'); - add_section ($S, 'postfixstop', 0, 'd', 'Postfix stop'); - add_section ($S, 'postfixrefresh', 0, 'd', 'Postfix refresh'); - add_section ($S, 'postfixwaiting', 0, 'd', 'Postfix waiting to terminate'); - end_section_group ($S, 'postfixstate'); - - - if ($Opts{'debug'} & Logreporters::D_SECT) { - print "\tSection table\n"; - printf "\t\t%s\n", (ref($_) eq 'HASH' ? $_->{NAME} : $_) foreach @Sections; - print "build_sect_table: exit\n" - } -} - -# XXX create array of defaults for detail <5, 5-9, >10 -sub init_defaults() { - map { $Opts{$_} = $Defaults{$_} unless exists $Opts{$_} } keys %Defaults; - if (! $Opts{'standalone'}) { - # LOGWATCH - # these take affect if no env present (eg. nothing in conf file) - # 0 to 4 nodelays - - if ($Opts{'detail'} < 5) { # detail 0 to 4, disable all supplimental reports - $Opts{'delays'} = 0; - $Opts{'postgrey_delays'} = 0; - } - } -} - - -# XXX ensure something is matched? -# XXX cache values so we don't have to substitute X for . each time -#match $dsn against list for best fit -sub get_reject_key($) { - my $reply = shift; - my $replyorig = $reply; - ($reply) = split / /, $reply; - for (my $i = 0; $i <= $#RejectPats; $i++) { - #print "TRYING: $RejectPats[$i]\n"; - # we'll allow extended DSNs to match (eg. 5.7.1 will match 5..) - if ($reply =~ /^$RejectPats[$i]/) { # no /o here, pattern varies - #print "MATCHED: orig: $replyorig, reply $reply matched pattern $RejectPats[$i], returning $RejectKeys[$i]\n"; - return $RejectKeys[$i]; - } - } - #print "NOT MATCHED: REPLY CODE: '$replyorig', '$reply'\n"; - return; -} - -# Replace bare reject limiters with specific reject limiters -# based on reject_reply_patterns -# -sub expand_bare_reject_limiters() -{ - # don't reorder the list of limiters. This breaks --nodetail followed by a - # bare reject such as --limit rejectrbl=10. Reordering is no longer necessary - # since process_limiters was instituted and using the special __none__ pseudo- - # limiter to indicate the position at which --nodefailt was found on the command - # line. - # my ($limiter, @reject_limiters, @non_reject_limiters); - my ($limiter, @new_list); - - # XXX check if limiter matches just one in rejectclasses - while ($limiter = shift @Limiters) { - if ($limiter =~ /^reject[^_]/) { - foreach my $reply_code (@RejectKeys) { - printf "bare_reject: \L$reply_code$limiter\n" if $Opts{'debug'} & Logreporters::D_VARS; - #push @reject_limiters, lc($reply_code) . $limiter; - push @new_list, lc($reply_code) . $limiter; - } - } - elsif ($limiter =~ /^(?:[45]\.\.|Warn)reject[^_]/) { - $limiter =~ s/^([45])\.\./$1xx/; - #push @reject_limiters, lc $limiter; - push @new_list, lc $limiter; - } - else { - #push @non_reject_limiters, $limiter; - push @new_list, $limiter; - } - } - #@Limiters = (@reject_limiters, @non_reject_limiters); - @Limiters = @new_list; -} - - -# Return a usage string, built from: -# arg1 + -# $usage_str + -# a string built from each usable entry in the @Sections table. -# reject patterns are special cased to minimize the number of -# command line options presented. -# -sub usage($) { - my $ret = ""; - $ret = "@_\n" if ($_[0]); - - $ret .= $usage_str; - my ($name, $desc, %reject_types); - foreach my $sect (get_usable_sectvars(@Sections, 0)) { - - if (my ($code,$rej) = ($sect->{NAME} =~ /^(...|warn)(reject.*)$/oi)) { - $rej = lc $rej; - next if (exists $reject_types{$rej}); - $reject_types{$rej}++; - $name = '[###]' . $rej; - $desc = '###' . substr($sect->{TITLE}, length($code)); - } - else { - $name = lc $sect->{NAME}; - $desc = $sect->{TITLE}; - } - $ret .= sprintf " --%-38s%s\n", "$name" . ' LEVEL', "$desc"; - } - $ret .= "\n"; - return $ret; -} - -1; - -# vi: shiftwidth=3 tabstop=3 syntax=perl et
--- a/common/logwatch/scripts_mysql Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -#!/usr/bin/perl -w -# -# $Id$ -# -# Logwatch service for mysqld error log -# To be placed in -# /etc/logwatch/scripts/mysql -# -# Processes all messages and summarizes them -# Each message is given with a timestamp and RMS -# -# (C) 2006 by Jeremias Reith <jr@terragate.net> -# Modified 2009 by Michael Baierl - - -use strict; -use Logwatch ':dates'; -use Time::Local; -use POSIX qw(strftime); - -my $date_format = '%y%m%d %H:%M:%S'; -my $filter = TimeFilter($date_format); -my $detail = exists $ENV{'LOGWATCH_DETAIL_LEVEL'} ? $ENV{'LOGWATCH_DETAIL_LEVEL'} : 0; - -# we do not use any Date:: package (or strptime) as they are probably not available -my %month2num = ( Jan => 0, Feb => 1, Mar => 2, Apr => 3, - May => 4, Jun => 5, Jul => 6, Aug => 7, - Sep => 8, Oct => 9, Nov => 10, Dec => 11 ); - -# array of message categories (we do not use a hash to keep the order) -# first element: catorory name -# second element: matching regexp ($1 should contain the message) -# third element: anonymous hash ref (stores message counts) -my @message_categories = (['Errors', qr/\[ERROR\] (.*)$/o, {}], - ['Note', qr/\[Note\] (.*)$/o, {}], - ['Other', qr/(.*)$/o, {}]); - -# counting messages -while(<>) { - my $line = $_; - # skipping messages that are not within the requested range - next unless $line =~ /^($filter)/o; - $1 =~ /(\d\d)(\d\d)(\d\d)\s+(\d+):(\d+):(\d+)/; - my $time; - - { - # timelocal is quite chatty - local $SIG{'__WARN__'} = sub {}; - $time = timelocal($6, $5, $4, $3, $2-1, $1); - } - - foreach my $cur_cat (@message_categories) { - if($line =~ /$cur_cat->[1]/) { - my $msgs = $cur_cat->[2]; - $msgs->{$1} = {count => '0', - first_occurrence => $time, - sum => 0, - sqrsum => 0} unless exists $msgs->{$1}; - $msgs->{$1}->{'count'}++; - # summing up timestamps and squares of timestamps - # in order to calculate the rms - # using first occurrence of message as offset in calculation to - # prevent an integer overflow - $msgs->{$1}->{'sum'} += $time - $msgs->{$1}->{'first_occurrence'}; - $msgs->{$1}->{'sqrsum'} += ($time - $msgs->{$1}->{'first_occurrence'}) ** 2; - last; - } - } -} - - -# generating summary -foreach my $cur_cat (@message_categories) { - # skipping non-requested message types - next unless keys %{$cur_cat->[2]}; - my ($name, undef, $msgs) = @{$cur_cat}; - print $name, ":\n"; - my $last_count = 0; - - # sorting messages by count - my @sorted_msgs = sort { $msgs->{$b}->{'count'} <=> $msgs->{$a}->{'count'} } keys %{$msgs}; - - foreach my $msg (@sorted_msgs) { - # grouping messages by number of occurrence - print "\n", $msgs->{$msg}->{'count'}, " times:\n" unless $last_count == $msgs->{$msg}->{'count'}; - my $rms = 0; - - - # printing timestamp - print '['; - - if($msgs->{$msg}->{'count'} > 1) { - # calculating rms - $rms = int(sqrt( - ($msgs->{$msg}->{'count'} * - $msgs->{$msg}->{'sqrsum'} - - $msgs->{$msg}->{'sum'}) / - ($msgs->{$msg}->{'count'} * - ($msgs->{$msg}->{'count'} - 1)))); - - print strftime($date_format, localtime($msgs->{$msg}->{'first_occurrence'}+int($rms/2))); - - print ' +/-'; - - # printing rms - if($rms > 86400) { - print int($rms/86400) , ' day(s)'; - } elsif($rms > 3600) { - print int($rms/3600) , ' hour(s)'; - } elsif($rms > 60) { - print int($rms/60) , ' minute(s)'; - } else { - print $rms, ' seconds'; - } - } else { - # we have got this message a single time - print strftime($date_format, localtime($msgs->{$msg}->{'first_occurrence'})); - } - - print '] ', $msg, "\n"; - $last_count = $msgs->{$msg}->{'count'}; - } - - print "\n"; -} -
--- a/common/logwatch/scripts_php Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -#!/usr/bin/perl -w -# -# $Id$ -# -# Logwatch service for php error logs -# To be placed in -# /etc/logwatch/scripts/php -# -# Processes all messages and summarizes them -# Each message is given with a timestamp and RMS -# -# (C) 2006 by Jeremias Reith <jr@terragate.net> -# Modified 2009 by Michael Baierl - - -use strict; -use Logwatch ':dates'; -use Time::Local; -use POSIX qw(strftime); - -# [Sun Dec 14 00:27:22 2008] -#my $date_format = '%d-%b-%Y %H:%M:%S'; -my $date_format = '... %b %d %H:%M:%S %Y'; -my $filter = TimeFilter($date_format); -my $detail = exists $ENV{'LOGWATCH_DETAIL_LEVEL'} ? $ENV{'LOGWATCH_DETAIL_LEVEL'} : 0; - -# we do not use any Date:: package (or strptime) as they are probably not available -my %month2num = ( Jan => 0, Feb => 1, Mar => 2, Apr => 3, - May => 4, Jun => 5, Jul => 6, Aug => 7, - Sep => 8, Oct => 9, Nov => 10, Dec => 11 ); - -# array of message categories (we do not use a hash to keep the order) -# first element: catorory name -# second element: matching regexp ($1 should contain the message) -# third element: anonymous hash ref (stores message counts) -my @message_categories = (['Fatal errors', qr/\] PHP Fatal error: (.*)$/o, {}], - ['Warnings', qr/\] PHP Warning: (.*)$/o, {}], - ['Notices', qr/\] PHP Notice: (.*)$/o, {}]); - -# skipping categories depending on detail level -pop(@message_categories) if $detail < 10; -pop(@message_categories) if $detail < 5; - -# counting messages -while(<>) { - my $line = $_; - $line =~ /(\w+) (\w+) (\d+) (\d+):(\d+):(\d+)\.\d{6} (\d+)/; - my $time; - - { - # timelocal is quite chatty - local $SIG{'__WARN__'} = sub {}; - $time = timelocal($6, $5, $4, $3, $month2num{$2}, $7-1900); - } - - foreach my $cur_cat (@message_categories) { - if($line =~ /$cur_cat->[1]/) { - my $msgs = $cur_cat->[2]; - $msgs->{$1} = {count => '0', - first_occurrence => $time, - sum => 0, - sqrsum => 0} unless exists $msgs->{$1}; - $msgs->{$1}->{'count'}++; - # summing up timestamps and squares of timestamps - # in order to calculate the rms - # using first occurrence of message as offset in calculation to - # prevent an integer overflow - $msgs->{$1}->{'sum'} += $time - $msgs->{$1}->{'first_occurrence'}; - $msgs->{$1}->{'sqrsum'} += ($time - $msgs->{$1}->{'first_occurrence'}) ** 2; - last; - } - } -} - - -# generating summary -foreach my $cur_cat (@message_categories) { - # skipping non-requested message types - next unless keys %{$cur_cat->[2]}; - my ($name, undef, $msgs) = @{$cur_cat}; - print $name, ":\n"; - my $last_count = 0; - - # sorting messages by count - my @sorted_msgs = sort { $msgs->{$b}->{'count'} <=> $msgs->{$a}->{'count'} } keys %{$msgs}; - - foreach my $msg (@sorted_msgs) { - # grouping messages by number of occurrence - print "\n", $msgs->{$msg}->{'count'}, " times:\n" unless $last_count == $msgs->{$msg}->{'count'}; - my $rms = 0; - - - # printing timestamp - print '['; - - if($msgs->{$msg}->{'count'} > 1) { - # calculating rms - $rms = int(sqrt( - ($msgs->{$msg}->{'count'} * - $msgs->{$msg}->{'sqrsum'} - - $msgs->{$msg}->{'sum'}) / - ($msgs->{$msg}->{'count'} * - ($msgs->{$msg}->{'count'} - 1)))); - - print strftime($date_format, localtime($msgs->{$msg}->{'first_occurrence'}+int($rms/2))); - - print ' +/-'; - - # printing rms - if($rms > 86400) { - print int($rms/86400) , ' day(s)'; - } elsif($rms > 3600) { - print int($rms/3600) , ' hour(s)'; - } elsif($rms > 60) { - print int($rms/60) , ' minute(s)'; - } else { - print $rms, ' seconds'; - } - } else { - # we have got this message a single time - print strftime($date_format, localtime($msgs->{$msg}->{'first_occurrence'})); - } - - print '] ', $msg, "\n"; - $last_count = $msgs->{$msg}->{'count'}; - } - - print "\n"; -} -
--- a/common/logwatch/services-fail2ban Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,196 +0,0 @@ -########################################################################## -# $Id: fail2ban 226 2014-09-09 11:07:27Z stefjakobs $ -########################################################################## -# $Log: fail2ban,v $ -# Revision 1.5b - IBBoard -# Patched up to cover fail2ban 0.10 -# -# Revision 1.5a - IBBoard -# Patched up to what we see on CentOS 6 w/fail2ban-0.9.2 -# -# Revision 1.5 2008/08/18 16:07:46 mike -# Patches from Paul Gear <paul at libertysys.com> -mgt -# -# Revision 1.4 2008/06/30 23:07:51 kirk -# fixed copyright holders for files where I know who they should be -# -# Revision 1.3 2008/03/24 23:31:26 kirk -# added copyright/license notice to each script -# -# Revision 1.2 2006/12/15 04:53:59 bjorn -# Additional filtering, by Willi Mann. -# -# Revision 1.1 2006/05/30 19:04:26 bjorn -# Added fail2ban service, written by Yaroslav Halchenko. -# -# Written by Yaroslav Halchenko <debian@onerussian.com> for fail2ban -# -########################################################################## - -######################################################## -## Copyright (c) 2008 Yaroslav Halchenko -## 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. -######################################################### - -use strict; -use Logwatch ':all'; - -my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0; -my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; -my $IgnoreHost = $ENV{'sshd_ignore_host'} || ""; -my $DebugCounter = 0; -my $ReInitializations = 0; -my @IptablesErrors = (); -my @ActionErrors = (); -my $NotValidIP = 0; # reported invalid IPs number -my @OtherList = (); - -my %ServicesBans = (); - -if ( $Debug >= 5 ) { - print STDERR "\n\nDEBUG: Inside Fail2Ban Filter \n\n"; - $DebugCounter = 1; -} - -while (defined(my $ThisLine = <STDIN>)) { - if ( $Debug >= 5 ) { - print STDERR "DEBUG($DebugCounter): $ThisLine"; - $DebugCounter++; - } - chomp($ThisLine); - if ( ($ThisLine =~ /..,... DEBUG: /) or - ($ThisLine =~ /..,... \S*\s*: DEBUG /) or # syntax of 0.7.? fail2ban - ($ThisLine =~ /..,... INFO: (Fail2Ban v.* is running|Exiting|Enabled sections:)/) or - ($ThisLine =~ /INFO\s+Log rotation detected for/) or - ($ThisLine =~ /INFO\s+Jail.+(?:stopped|started|uses poller|uses pyinotify)/) or - ($ThisLine =~ /INFO\s+Changed logging target to/) or - ($ThisLine =~ /INFO\s+Creating new jail/) or - ($ThisLine =~ /..,... \S+\s*: INFO\s+(Set |Socket|Exiting|Gamin|Created|Added|Using)/) or # syntax of 0.7.? fail2ban - ($ThisLine =~ /..,... WARNING: Verbose level is /) or - ($ThisLine =~ /..,... WARNING: Restoring firewall rules/) or - ($ThisLine =~ /WARNING Determined IP using DNS Lookup: [^ ]+ = \['[^']+'\]/) or - ($ThisLine =~ /INFO\s+(Stopping all jails|Exiting Fail2ban)/) or - ($ThisLine =~ /INFO\s+Initiated 'pyinotify' backend/) or - ($ThisLine =~ /INFO\s+(Added logfile = .*|Set maxRetry = \d+|Set findtime = \d+|Set banTime = \d+)/) - ) - { - if ( $Debug >= 6 ) { - print STDERR "DEBUG($DebugCounter): line ignored\n"; - } - } elsif ( ($ThisLine =~ /INFO\s+\[[^\]]+\] Found [0-9\.]+/) ) { - if ( $Debug >= 6 ) { - print STDERR "DEBUG($DebugCounter): line ignored\n"; - } - } elsif ( my ($Service,$Action,$Host) = ($ThisLine =~ m/(?:WARNING|NOTICE):?\s+\[?(.*?)[]:]?\s(?:Restore )?(Ban|Unban)[^\.]* (\S+)/)) { - if ( $Debug >= 6 ) { - print STDERR "DEBUG($DebugCounter): Found $Action for $Service from $Host\n"; - } - $ServicesBans{$Service}{$Host}{$Action}++; - $ServicesBans{$Service}{"(all)"}{$Action}++; - } elsif ( my ($Service,$Host,$NumFailures) = ($ThisLine =~ m/INFO: (\S+): (.+) has (\d+) login failure\(s\). Banned./)) { - if ($Debug >= 4) { - print STDERR "DEBUG: Found host $Host trying to access $Service - failed $NumFailures times\n"; - } - push @{$ServicesBans{$Service}{$Host}{'Failures'}}, $NumFailures; - } elsif ( my ($Service,$Host) = ($ThisLine =~ m/ ERROR:\s(.*):\s(\S+)\salready in ban list/)) { - $ServicesBans{$Service}{$Host}{'AlreadyInTheList'}++; - } elsif ( my ($Service,$Host) = ($ThisLine =~ m/(?:INFO|WARNING|NOTICE)\s*\[(.*)\]\s*(\S+)\s*already banned/)) { - $ServicesBans{$Service}{$Host}{'AlreadyInTheList'}++; - } elsif ( my ($Service,$Host) = ($ThisLine =~ m/ WARNING:\s(.*):\sReBan (\S+)/)) { - $ServicesBans{$Service}{$Host}{'ReBan'}++; - } elsif ($ThisLine =~ / ERROR:?\s*(Execution of command )?\'?iptables/) { - push @IptablesErrors, "$ThisLine\n"; - } elsif ($ThisLine =~ /ERROR.*returned \d+$/) { - push @ActionErrors, "$ThisLine\n"; - } elsif (($ThisLine =~ /..,... WARNING: \#\S+ reinitialization of firewalls/) or - ($ThisLine =~ / ERROR\s*Invariant check failed. Trying to restore a sane environment/)) { - $ReInitializations++; - } elsif ($ThisLine =~ /..,... WARNING: is not a valid IP address/) { - # just ignore - this will be fixed within fail2ban and is harmless warning - } elsif ($ThisLine =~ /Flush ticket\(s\)/) { - # just ignore - this is fail2ban 0.10 doing a quick shutdown/restart - } - else - { - # Report any unmatched entries... - push @OtherList, "$ThisLine\n"; - } -} - -########################################################### - - -if (keys %ServicesBans) { - printf("\nBanned services with Fail2Ban: Bans:Unbans\n"); - foreach my $service (sort {$a cmp $b} keys %ServicesBans) { - printf(" %-55s [%3d:%-3d]\n", "$service:", - $ServicesBans{$service}{'(all)'}{'Ban'}, - $ServicesBans{$service}{'(all)'}{'Unban'}); - delete $ServicesBans{$service}{'(all)'}; - my $totalSort = TotalCountOrder(%{$ServicesBans{$service}}, \&SortIP); - if ($Detail >= 5) { - foreach my $ip (sort $totalSort keys %{$ServicesBans{$service}}) { - my $name = LookupIP($ip); - printf(" %-53s %3d:%-3d\n", - $name, - $ServicesBans{$service}{$ip}{'Ban'}, - $ServicesBans{$service}{$ip}{'Unban'}); - if (($Detail >= 10) and ($ServicesBans{$service}{$ip}{'Failures'}>0)) { - print " Failed "; - foreach my $fails (@{$ServicesBans{$service}{$ip}{'Failures'}}) { - print " $fails"; - } - print " times"; - printf("\n %d Duplicate Ban attempts", $ServicesBans{$service}{$ip}{'AlreadyInTheList'}) ; - printf("\n %d ReBans due to rules reinitilizations", $ServicesBans{$service}{$ip}{'ReBan'}) ; - print "\n"; - } - } - } - } -} - - -if ($Detail>0) { - if ($#IptablesErrors > 0) { - printf("\n%d faulty iptables invocation(s)", $#IptablesErrors); - if ($Detail > 5) { - print ":\n"; - print @IptablesErrors ; - } - } - if ($#ActionErrors > 0) { - printf("\n%d error(s) returned from actions", $#ActionErrors); - if ($Detail > 5) { - print ":\n"; - print @ActionErrors ; - } - } - if ($ReInitializations > 0) { - printf("\n%d fail2ban rules reinitialization(s)", $ReInitializations); - } - if ($#OtherList >= 0) { - print "\n**Unmatched Entries**\n"; - print @OtherList; - } -} - -exit(0); - -# vi: shiftwidth=3 tabstop=3 syntax=perl et -# Local Variables: -# mode: perl -# perl-indent-level: 3 -# indent-tabs-mode: nil -# End:
--- a/common/logwatch/services-http-error.conf Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -# Add Apache 2.4-compliant log support -LogFile = http-error-24 -
--- a/common/logwatch/services_mysql.conf Sun Mar 08 19:56:26 2020 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -# -# Service definition for MySQL error log -# -# File to be placed in -# /etc/logwatch/conf/services/mysql.conf -# - -Title = mysqld - -# Which logfile group... -LogFile = mysql - -# -Detail = High -
--- a/manifests/templates.pp Sun Mar 08 19:56:26 2020 +0000 +++ b/manifests/templates.pp Sun Mar 15 16:51:11 2020 +0000 @@ -297,47 +297,26 @@ file { '/etc/cron.daily/0logwatch': source => 'puppet:///common/0logwatch'; } - file { '/etc/logwatch/scripts/shared/': + $logwatch_dirs = [ + '/etc/logwatch/', + '/etc/logwatch/conf/', + '/etc/logwatch/conf/logfiles/', + '/etc/logwatch/conf/services/', + ] + file { $logwatch_dirs: ensure => directory, } - file { '/etc/logwatch/scripts/services/fail2ban': - source => 'puppet:///common/logwatch/services-fail2ban', - } - file { '/etc/logwatch/scripts/services/named': - source => 'puppet:///common/logwatch/named', - } - file { '/etc/logwatch/scripts/services/http-error': - source => 'puppet:///common/logwatch/http-error', - } - file { '/etc/logwatch/scripts/services/php': - source => 'puppet:///common/logwatch/scripts_php', - } - file { '/etc/logwatch/scripts/services/mysql': - source => 'puppet:///common/logwatch/scripts_mysql', - } - file { '/etc/logwatch/scripts/services/dovecot': - source => 'puppet:///common/logwatch/dovecot', - } - file { '/etc/logwatch/scripts/services/postfix': - source => 'puppet:///common/logwatch/postfix', - } - file { '/etc/logwatch/scripts/shared/applyhttperrordate': - source => 'puppet:///common/logwatch/applyhttperrordate', - } file { '/etc/logwatch/conf/logwatch.conf': content => 'Detail = Med', } file { '/etc/logwatch/conf/logfiles/http.conf': content => 'LogFile = apache/access_*.log', } - file { '/etc/logwatch/conf/logfiles/http-error-24.conf': - source => 'puppet:///common/logwatch/log-http-error.conf', + file { '/etc/logwatch/conf/logfiles/http-error.conf': + source => 'puppet:///common/logwatch/logfiles_http-error.conf', } - file { '/etc/logwatch/conf/logfiles/http-error.conf': - ensure=> absent, - } - file { '/etc/logwatch/conf/services/http-error.conf': - source => 'puppet:///common/logwatch/services-http-error.conf', + file { '/etc/logwatch/conf/logfiles/mysql.conf': + source => 'puppet:///common/logwatch/logfiles_mysql.conf', } file { '/etc/logwatch/conf/logfiles/php.conf': source => 'puppet:///common/logwatch/logfiles_php.conf', @@ -345,12 +324,6 @@ file { '/etc/logwatch/conf/services/php.conf': source => 'puppet:///common/logwatch/services_php.conf', } - file { '/etc/logwatch/conf/logfiles/mysql.conf': - source => 'puppet:///common/logwatch/logfiles_mysql.conf', - } - file { '/etc/logwatch/conf/services/mysql.conf': - source => 'puppet:///common/logwatch/services_mysql.conf', - } } #Our web server with our configs, not just a stock one