]> gitweb.michael.orlitzky.com - postfix-logwatch.git/blobdiff - postfix-logwatch
Changes: add the one change in v1.40.05.
[postfix-logwatch.git] / postfix-logwatch
index 1e58a95c3366908284c2012b2d15ffaa6ae36f95..cc37b877722b6ded348cebf88c4b5e6b3eb9be6f 100644 (file)
@@ -1,23 +1,14 @@
 #!/usr/bin/perl -T
 
 ##########################################################################
 #!/usr/bin/perl -T
 
 ##########################################################################
-# Postfix-logwatch: written and maintained by:
-#
-#    Mike "MrC" Cappella <mike (at) cappella (dot) us>
-#      http://logreporters.sourceforge.net/
+# Postfix-logwatch: written by Mike Capella, and maintained by Michael
+# Orlitzky <michael@orlitzky.com>.
 #
 # Please send all comments, suggestions, bug reports regarding this
 #
 # 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
+# program to the email address above.
 #
 #######################################################
 #
 #######################################################
-### 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
 ### Covered under the included MIT/X-Consortium License:
 ###    http://www.opensource.org/licenses/mit-license.php
 ### All modifications and contributions by other persons to
 ### 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
 ### 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.
+### contributions.
 ##########################################################
 
 ##########################################################
 
-##########################################################################
-# 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"
 #
 
 ##########################################################################
 #
 # Test data included via inline comments starting with "#TD"
 #
 
-#use Devel::Size qw(size total_size);
 package Logreporters;
 use 5.008;
 use strict;
 package Logreporters;
 use 5.008;
 use strict;
@@ -55,7 +33,7 @@ use warnings;
 no warnings "uninitialized";
 use re 'taint';
 
 no warnings "uninitialized";
 use re 'taint';
 
-our $Version          = '1.40.03';
+our $Version          = '1.40.05';
 our $progname_prefix  = 'postfix';
 
 # Specifies the default configuration file for use in standalone mode.
 our $progname_prefix  = 'postfix';
 
 # Specifies the default configuration file for use in standalone mode.
@@ -66,7 +44,9 @@ my $re_QID_s   = qr/[A-Z\d]+/;
 my $re_QID_l   = qr/(?:NOQUEUE|[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ\d]+)/;
 our $re_QID;
 
 my $re_QID_l   = qr/(?:NOQUEUE|[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ\d]+)/;
 our $re_QID;
 
-our $re_DSN     = qr/(?:(?:\d{3})?(?: ?\d\.\d\.\d)?)/;
+# The enhanced status codes can contain two-digit (or more) numbers;
+# for example, "550 5.7.23".
+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
 our $re_DDD     = qr/(?:(?:conn_use=\d+ )?delay=-?[\d.]+(?:, delays=[\d\/.]+)?(?:, dsn=[\d.]+)?)/;
 
 #MODULE: ../Logreporters/Utils.pm
@@ -1276,8 +1256,8 @@ sub print_unmatched_report() {
 
        1978   SpamAssassin bypassed 
          18   Released from quarantine 
 
        1978   SpamAssassin bypassed 
          18   Released from quarantine 
-       1982   Whitelisted           
-          3   Blacklisted           
+       1982   Allowlisted           
+          3   Denylisted            
          12   MIME error            
          51   Bad header (debug supplemental) 
          28   Extra code modules loaded at runtime 
          12   MIME error            
          51   Bad header (debug supplemental) 
          28   Extra code modules loaded at runtime 
@@ -1378,8 +1358,17 @@ sub print_summary_report (\@) {
                      $$divisor == $Totals{$keyname} ? 100.00 : $Totals{$keyname} * 100 / $$divisor;
             }
             else {
                      $$divisor == $Totals{$keyname} ? 100.00 : $Totals{$keyname} * 100 / $$divisor;
             }
             else {
-               push @{$lines[$cur_level]}, 
-                  sprintf "$fmt  %-23s $extra\n", $total, $desc, commify ($Totals{$keyname});
+               my $new_line;
+               if ($extra eq '') {
+                   $new_line = sprintf("$fmt  %-23s \n", $total, $desc);
+               }
+               else {
+                   $new_line = sprintf("$fmt  %-23s $extra\n",
+                                       $total,
+                                       $desc,
+                                       commify ($Totals{$keyname}));
+               }
+               push @{$lines[$cur_level]}, $new_line
             }
          }
       }
             }
          }
       }
@@ -2704,10 +2693,8 @@ LINE: while ( <> ) {
 
    our $service_name = $3;
    my ($mailhost,$server_name,$p1) = ($1,$2,$4);
 
    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;
 
    $service_name = $server_name unless $service_name;
-   #print "service_name: $service_name\n";
 
    # ignored postfix services...
    next if $service_name eq 'postlog';
 
    # ignored postfix services...
    next if $service_name eq 'postlog';
@@ -2787,7 +2774,6 @@ sys     0m3.005s
    # 5:25
    foreach (@policy_services) {
       if ($service_name =~ $_->[0]) {
    # 5:25
    foreach (@policy_services) {
       if ($service_name =~ $_->[0]) {
-         #print "Calling policy service helper: $service_name:('$p1')\n";
          &{$_->[1]}($p1);
          next LINE;
       }
          &{$_->[1]}($p1);
          next LINE;
       }
@@ -2859,7 +2845,6 @@ sys     0m3.005s
       # 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)) {
       # 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;
       }
 
          next;
       }
 
@@ -2897,14 +2882,12 @@ sys     0m3.005s
          my $action = $1;
          $p1 = substr($p1, length($action) + 2);
 
          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;
          }
          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";
+         my ($stage,$host,$hostip,$p1) = ($1,$2,$3,$4);
+         my ($efrom,$eto,$proto,$helo) = strip_ftph($p1);
 
 # 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>
 
 # 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>
@@ -2962,12 +2945,11 @@ sys     0m3.005s
                inc_unmatched('reject1');
                next;
             }
                inc_unmatched('reject1');
                next;
             }
-            ($dsn,$p1) = ($1,$2);                        #print "dsn: $dsn, p1: \"$p1\"\n";
+            ($dsn,$p1) = ($1,$2);
             $fmthost = formathost($hostip,$host);
 
             # reject_warning override temp or perm reject types
             $rej_type = ($action eq 'reject_warning' ? 'warn' : get_reject_key($dsn));
             $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 ($Collecting{'byiprejects'} and substr($rej_type,0,1) eq '5') {
                $Counts{'byiprejects'}{$fmthost}++;
@@ -3144,7 +3126,6 @@ sys     0m3.005s
             else {                  $trigger =~ s/^<(.+)>$/$1/; }
             $reason  = '*unavailable' if ($reason eq '');
             $fmthost = formathost ($hostip,$host);
             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
 
             # reason -> subject text
             #           subject -> "Helo command"           : smtpd_helo_restrictions
@@ -3162,7 +3143,6 @@ sys     0m3.005s
 
             my ($subject, $text) =
                ($reason =~ /^((?:Recipient|Sender) address|(?:Unverified )?Client host|Client certificate|(?:Helo|Etrn|Data) command|End-of-data) (.+)$/o);
 
             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'});
 
             if ($action eq 'filter') {
                $Totals{'filtered'}++; next unless ($Collecting{'filtered'});
@@ -3369,7 +3349,6 @@ sys     0m3.005s
             #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")
 
             #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
 
             # See same code elsewhere "Note: Bounce" 
             ### local bounce
@@ -3525,7 +3504,6 @@ sys     0m3.005s
       ($site,$reason) = ($5 =~ /^(.+?)(?:$|(?:[.,] )(.*))/);
       $reason =~ s/^reason: // if ($reason);
       $rej_type = ($rej_type =~ /_warning/ ? 'warn' : get_reject_key($dsn));
       ($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)
       # Note: similar code above: search RejectRBL
 
       # This section required: postfix didn't always log QID (eg. postfix 1.1)
@@ -3548,7 +3526,7 @@ sys     0m3.005s
    # proxy-reject, proxy-accept
    elsif ($p1 =~ s/^proxy-(reject|accept): ([^:]+): //) {
       # 2.7
    # 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-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>
 
       #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>
 
@@ -3556,15 +3534,14 @@ sys     0m3.005s
 
       my ($stage) = ($2);
       my ($efrom,$eto,$proto,$helo) = strip_ftph($p1);
 
       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;
 
       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).
+
+      # 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;
       }
       if (! defined $dsn) {
          next;
       }
@@ -3702,8 +3679,6 @@ if ($Opts{'summary'}) {
 # Print the Detail report, if detail is sufficiently high
 #
 if ($Opts{'detail'} >= 5) {
 # 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'}) {
    print_detail_report(@Sections);
 
    if ($Opts{'delays'}) {
@@ -3721,12 +3696,6 @@ if ($Opts{'detail'} >= 5) {
 
 }
 
 
 }
 
-# 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();
 # Finally, print any unmatched lines
 #
 print_unmatched_report();
@@ -3933,6 +3902,7 @@ sub create_ignore_list() {
    push @ignore_list, qr/^reject_invalid_hostname: /;
    push @ignore_list, qr/^cfg_get_/;
    push @ignore_list, qr/^sacl_check: /;
    push @ignore_list, qr/^reject_invalid_hostname: /;
    push @ignore_list, qr/^cfg_get_/;
    push @ignore_list, qr/^sacl_check: /;
+   push @ignore_list, qr/^breaking line > 998 bytes with /;
 
    # non-anchored
    #push @ignore_list, qr/: Greylisted for /;
 
    # non-anchored
    #push @ignore_list, qr/: Greylisted for /;
@@ -3951,9 +3921,7 @@ sub in_ignore_list($) {
    my $line = shift;
 
    foreach (@ignore_list) {
    my $line = shift;
 
    foreach (@ignore_list) {
-      #return 1 if $line =~ /$_/;
       if ($line =~ /$_/) {
       if ($line =~ /$_/) {
-         #$IGNORED{$_}++;
          return 1;
       }
    }
          return 1;
       }
    }
@@ -4014,7 +3982,7 @@ sub process_delivery_attempt ($ $ $ $) {
 }
 
 # Processes postfix/bounce messages
 }
 
 # Processes postfix/bounce messages
-# 
+#
 sub postfix_bounce($) {
    my $line = shift;
    my $type;
 sub postfix_bounce($) {
    my $line = shift;
    my $type;
@@ -4044,7 +4012,7 @@ sub postfix_bounce($) {
 
 # Processes postfix/cleanup messages
 #   cleanup always has a QID
 
 # Processes postfix/cleanup messages
 #   cleanup always has a QID
-# 
+#
 sub postfix_cleanup($) {
    my $line = shift;
    my ($qid,$reply,$fmthost,$reject_name);
 sub postfix_cleanup($) {
    my $line = shift;
    my ($qid,$reply,$fmthost,$reject_name);
@@ -4062,7 +4030,7 @@ sub postfix_cleanup($) {
 
    ### cleanup bounced messages (always_bcc, recipient_bcc_maps, sender_bcc_maps)
    # Note: Bounce
 
    ### cleanup bounced messages (always_bcc, recipient_bcc_maps, sender_bcc_maps)
    # Note: Bounce
-   #   See same code elsewhere "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) {
    #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) {
@@ -4092,7 +4060,6 @@ sub postfix_cleanup($) {
 
    # *header_checks and body_checks
    elsif (header_body_checks($line)) {
 
    # *header_checks and body_checks
    elsif (header_body_checks($line)) {
-      #print "cleanup: header_body_checks\n";
       return;
    }
 
       return;
    }
 
@@ -4148,8 +4115,6 @@ sub header_body_checks($)
 
    my ($action,$part,$p3) = ($1,$2,$3);
 
 
    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 ...
    my ($trigger,$host,$eto,$p4,$fmthost,$reject_name);
    # $re_QID: reject: body ...
    # $re_QID: reject: header ...
@@ -4164,7 +4129,7 @@ sub header_body_checks($)
       #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:   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 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 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...
@@ -4187,7 +4152,7 @@ sub header_body_checks($)
 
    else {
       # smtp_body_checks, smtp_header_checks, smtp_mime_header_checks, smtp_nested_header_checks (postfix >= 2.5)
 
    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> 
+      #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';
 
 
       $trigger = $p3; $host = ''; $eto = ''; $p4 = $part eq 'body' ? 'smtp_body_checks' : 'smtp_*header_checks';
 
@@ -4195,7 +4160,6 @@ sub header_body_checks($)
       #return 1;
    }
 
       #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 '');
    $trigger =~ s/\s+/ /g;
    $trigger = '*unknown reason'    if ($trigger eq '');
    $eto     = '*unknown'           if ($eto     eq '');
@@ -4287,18 +4251,15 @@ sub milter_common($) {
 #   84B82AC8B3: milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 5.7.1 Blocked
 
    my ($efrom,$eto,$proto,$helo) = strip_ftph($line);
 #   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);
    $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;
 
       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);
 
          if ($Collecting{'byiprejects'} and substr($dsn,0,1) eq '5') {
             $fmthost = formathost($hostip,$host);
@@ -4348,7 +4309,8 @@ sub postfix_postscreen {
       $line =~ /discarding EHLO keywords: / or
       $line =~ /: discard_mask / or
       $line =~ /: sq=\d+ cq=\d+ event/ or
       $line =~ /discarding EHLO keywords: / or
       $line =~ /: discard_mask / or
       $line =~ /: sq=\d+ cq=\d+ event/ or
-      $line =~ /: replacing command "/
+      $line =~ /: replacing command "/ or
+      $line =~ /^(DATA|BDAT) without valid RCPT/
    );
 
 
    );
 
 
@@ -4358,7 +4320,12 @@ sub postfix_postscreen {
        ($line =~ /^(HANGUP) (?:after \S+)? from \[([^]]+)\](?::\d+)?/)) {
       $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++  if $Collecting{'postscreen'};
    }
        ($line =~ /^(HANGUP) (?:after \S+)? from \[([^]]+)\](?::\d+)?/)) {
       $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++  if $Collecting{'postscreen'};
    }
-   elsif ($line =~ /^(WHITELISTED|BLACKLISTED|PASS \S+) \[([^]]+)\](?::\d+)?$/) {
+   elsif ($line =~ /^((ALLOW|WHITE|BLACK|DENY)LISTED|PASS \S+) \[([^]]+)\](?::\d+)?$/) {
+      # This will display two separate counts for e.g. "allowlisted"
+      # and "whitelisted" if you change your configuration in the
+      # middle of the day, but I don't see that as a huge problem.
+      #
+      # ALLOWLISTED [40.92.75.48]:17085
       # PASS NEW [192.168.0.2]:12345
       # PASS OLD [192.168.0.3]:12345
       $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++  if $Collecting{'postscreen'};
       # PASS NEW [192.168.0.2]:12345
       # PASS OLD [192.168.0.3]:12345
       $Counts{'postscreen'}{lc $1}{$2}{$END_KEY}++  if $Collecting{'postscreen'};
@@ -4388,19 +4355,16 @@ sub postfix_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 =~ /^(NOQUEUE: )?reject: (connect|CONNECT) from \[([^]]+)\](?::\d+)?: (.+)$/) {
+       # NOQUEUE: reject: CONNECT from [192.168.0.1]:7197: too many connections
+       # NOQUEUE: reject: CONNECT from [192.168.0.1]:39410: all server ports busy
+       # reject: connect from [192.168.0.1]:21225: all screening ports busy
+      $Counts{'postscreen'}{'reject'}{"\u$4"}{$3}{$END_KEY}++      if $Collecting{'postscreen'};
    }
 
    }
 
-   elsif ($line =~ /^(?:WHITELIST VETO) \[([^]]+)\](?::\d+)?$/) {
+   elsif ($line =~ /^(?:(WHITE|ALLOW)LIST VETO) \[([^]]+)\](?::\d+)?$/) {
       # WHITELIST VETO [192.168.0.8]:43579
       # WHITELIST VETO [192.168.0.8]:43579
-      $Counts{'postscreen'}{'whitelist veto'}{$1}{$END_KEY}++  if $Collecting{'postscreen'};
+      $Counts{'postscreen'}{'allowlist veto'}{$1}{$END_KEY}++  if $Collecting{'postscreen'};
    }
 
    elsif ($line =~ /^(entering|leaving) STRESS mode with (\d+) connections$/) {
    }
 
    elsif ($line =~ /^(entering|leaving) STRESS mode with (\d+) connections$/) {
@@ -4482,7 +4446,8 @@ sub postfix_fatal($) {
       $Totals{'fatalfiletoobig'}++;
 
 
       $Totals{'fatalfiletoobig'}++;
 
 
-   # XXX its not clear this is at all useful - consider falling through to last case
+   # 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
    } 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
@@ -4737,8 +4702,8 @@ sub postfix_warning($) {
       #TDsd warning: 009314BD9E: read timeout on cleanup socket
       $warning =~ s/^$re_QID: (read timeout on \S+ socket)/$1/;
 
       #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 
+      #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/;
 
 =cut
       $warning =~ s/^(Read failed in network_biopair_interop) with .*$/$1/;
 
 =cut
@@ -4789,8 +4754,6 @@ sub cleanhostreply($ $ $ $) {
    my $fmtdhost = '';
    my ($r1, $r2, $dsn, $msg, $host, $event);
 
    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...
    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...
@@ -4823,11 +4786,9 @@ sub cleanhostreply($ $ $ $) {
           or $r1 =~ /(?:no such user|user unknown)/i
          )
       {
           or $r1 =~ /(?:no such user|user unknown)/i
          )
       {
-         #print "UNKNOWN RECIP: $r1\n";
          $r1 = 'Unknown recipient';
       }
       elsif ($r1 =~ /greylisted/oi) {
          $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) {
          $r1 = 'Recipient greylisted';
       }
       elsif ($r1 =~ /^Message temporarily deferred - (\d\.\d+\.\d+)\. Please refer to (.+)$/o) {
@@ -4835,65 +4796,54 @@ sub cleanhostreply($ $ $ $) {
          $dsn = "$dsn $1"; $r1 = "see $2";
       }
       elsif ($r1 =~ /^Resources temporarily not available - Please try again later \[#(\d\.\d+\.\d+)\]\.$/o) {
          $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]. 
+         #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) {
          $dsn = "$dsn $1"; $r1 = "resources not available";
       }
       elsif ($r1 =~ /^Message temporarily deferred - (\[\d+\])/o) {
-         # Yahoo: 451 Message temporarily deferred - [160] 
+         # Yahoo: 451 Message temporarily deferred - [160]
          $dsn = "$dsn $1"; $r1 = '';
       }
    }
 
    elsif ($hostreply =~ /^connect to (\S+): (.*)$/o) {
          $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;
       $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";
       }
       #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) {
       $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)
       $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)) {
       $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) {
       $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) {
       $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) {
       $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) {
       $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 {
       $host = $1; $r1 = 'message too big';
    }
    else {
-      #print "UNMATCH: $hostreply\n";
       $r1 = $hostreply;
    }
 
       $r1 = $hostreply;
    }
 
-   #print "R1: $r1, R2: $r2\n";
    $r1 =~ s/for name=\Q$domain\E //ig;
 
    if ($host eq '') {
    $r1 =~ s/for name=\Q$domain\E //ig;
 
    if ($host eq '') {
@@ -4922,14 +4872,11 @@ sub cleanhostreply($ $ $ $) {
 #
 sub strip_ftph($) {
    my ($helo, $proto, $to, $from);
 #
 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';
 
    $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);
 }
 
    return ($from,$to,$proto,$helo);
 }
 
@@ -4980,7 +4927,7 @@ sub init_getopts_table() {
 #   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.
 #   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.
 #
 #
 # Sections can be freely reordered if desired, but maintain proper group nesting.
 #
 #
@@ -4993,7 +4940,7 @@ sub init_getopts_table() {
 # require special-case processing to distinguish 4xx temporary rejects from 5xx
 # permanent rejects in various Totals{'totalrejects*'} counts, and in the
 # Totals{'totalrejects'} tally.
 # 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) {
 # Sections can be freely reordered if desired.
 sub build_sect_table() {
    if ($Opts{'debug'} & Logreporters::D_SECT) {
@@ -5053,8 +5000,9 @@ sub build_sect_table() {
    add_section ($S, 'totalacceptplusreject',       0, 'd', 'Total',                             \$Totals{'totalacceptplusreject'});
    end_section_group ($S, 'acceptreject', $sep1);
 
    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'}
+   # 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) {
    @RejectPats = ();
    foreach my $rejpat (split /[ ,]/, $Opts{'reject_reply_patterns'}) {
       if ($rejpat !~ /^(warn|[45][\d.]{2})$/io) {
@@ -5220,18 +5168,16 @@ sub get_reject_key($) {
    my $replyorig = $reply;
    ($reply) = split / /, $reply;
    for (my $i = 0; $i <= $#RejectPats; $i++) {
    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
       # 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];
       }
    }
          return $RejectKeys[$i];
       }
    }
-   #print "NOT MATCHED: REPLY CODE: '$replyorig', '$reply'\n";
+
    return;
 }
 
    return;
 }
 
-# Replace bare reject limiters with specific reject limiters 
+# Replace bare reject limiters with specific reject limiters
 # based on reject_reply_patterns
 #
 sub expand_bare_reject_limiters()
 # based on reject_reply_patterns
 #
 sub expand_bare_reject_limiters()