#!/local/paths/perl -wT ## # smreject.pl - Processes rejected message entries from sendmail logfile. ## # Ver 1.12 by Ted George (tgeorge@kcnet.com) # Sep 18, 1997 # # usage: smreject.pl [-t top#] [-f logfile] [-j] # # -tn list top n hosts or addresses for each ruleset. # n = 0 for no detail by host or email address. # # -flogfile use the given logfile instead of the default one. # # -j use the "junk file" to provide further information. # # -m addr mail the output to the given address # # Ver 1.12a (unofficial, Anne Bennett ) # Make "junk" optional; turn on iff opt_j. (Oct 22, 1997) # Initialize variables to avoid perl complaints if # certain counters are never incremented. (Oct 22, 1997) # Comment out never-used assignments. (Oct 22, 1997) # Highlight "configuration section". (Oct 22, 1997) # Initialize $top if not set with "-t". (Oct 22, 1997) # Added an option to change the logfile used. (Oct 22, 1997) # Print nothing if no rejects were found. (Oct 22, 1997) # Added option to mail output. (Oct 23, 1997) # # Ver 1.12 # Count for junk file rejections included check_mail # errors. (Sep 24, 1997) # # Ver 1.11 Check_mail rejections from domain names were # not properly matched. (Sep 18, 1997) # # Ver 1.1 Cleaned up output slightly. Minor fixes including # the unknown category for entries that don't match # any items in junk file. (Sep 10, 1997) # # Ver 1.0 Original release (Sep 5, 1997) # # ======================================================= # change these locations to match your system # # location of sendmail log file $logfile = "/local/var/log/system/maillog.1"; # location of junk text file $junk = "/dev/null"; # mail program which accepts "-s subject", address on command line, # and message on standard input. $mailprog = "mailx"; # path, used to call "cut" and the mail program $ENV{PATH} = "/local/paths:/usr/bin:/bin:/usr/sbin:/sbin"; # ======================================================= # initialize variables $check_relay = 0; $check_rcpt = 0; $check_mail = 0; $check_relay = 0; $check_mail = 0; $mail_errors = 0; # parse command-line options require "getopts.pl"; &Getopts("f:jm:t:"); $logfile = "$opt_f" if $opt_f; $dojunk = "$opt_j" if $opt_j; $mailto = "$opt_m" if $opt_m; $top = 0; $top = "$opt_t" if $opt_t; if ($mailto) { # Sanitize "mailto" to satisfy "-T": address should contain # nothing but "word" characters (alphabetics, numerics, and # underscores), a hyphen, an at sign, a comma, or a dot. if ($mailto =~ /^([-\@\w,.]+)$/) { $mailto = $1; } else { die "Bad characters in mail address: $mailto"; } $real_stdout = select(); open(MAILIT, "| $mailprog -s 'smreject results' $mailto") or die("Cannot fork $mailprog"); select(MAILIT); } if ($dojunk) { @junk = `cut -d" " -f1 $junk`; chop(@junk); } open(INPUT, "$logfile") || die "Can't open $logfile."; while () { $first = $_ if ! $first; $last = $_; next if !/check_/; $ruleset = $1 if (/ruleset=([^,]+),/); if ($ruleset =~ /check_relay/) { $check_relay++; # fix "used only once" warning # $arg2_relay{$check_relay} = $1 if (/arg2=([^,]+),/); /arg2=([^,]+),/; $count_arg2_relay{$1}++; if ($dojunk) { $junk_entry = &findIP($1); $count_junk{$junk_entry}++ if $junk_entry; } # fix "used only once" warning # $arg1_relay{$check_relay} = $1 if (/arg1=([^,]+),/); /arg1=([^,]+),/; if ($dojunk) { if (! $junk_entry) { $junk_entry = &findhost($1); $count_junk{$junk_entry}++ if $junk_entry; } $count_junk{"unknown"}++ if ! $junk_entry; } } if ($ruleset =~ /check_rcpt/) { $check_rcpt++; # fix "used only once" warning # $arg1_rcpt{$check_rcpt} = $1 if (/arg1=([^,]+),/); /arg1=([^,]+),/; $count_arg1_rcpt{$1}++; # fix "used only once" warning # $relay_rcpt{$check_rcpt} = $1 if (/relay=([^,]+),/); /relay=([^,]+),/; $count_relay_rcpt{$1}++; } if ($ruleset =~ /check_mail/) { $check_mail++; # fix "used only once" warning # $arg1_mail{$check_mail} = $1 if (/arg1=([^,]+),/); /arg1=([^,]+),/; $count_arg1_mail{$1}++; if ($dojunk) { $junk_entry = &findhost($1); $count_junk{$junk_entry}++ if $junk_entry; if (! $junk_entry) { $junk_entry = &findemail($1); $count_junk{$junk_entry}++ if $junk_entry; } if (! $junk_entry) { $junk_entry = "invalid host name" if (/... invalid host name/); $junk_entry = "illegal MAIL FROM" if (/... illegal MAIL FROM/); $junk_entry = "unresolvable host name" if (/... unresolvable host name/); $count_junk{$junk_entry}++ if $junk_entry; $mail_errors++ if $junk_entry; } $count_junk{"unknown"}++ if ! $junk_entry; } # fix "used only once" warning # $relay_mail{$check_mail} = $1 if (/relay=([^,]+),/); /relay=([^,]+),/; $count_relay_mail{$1}++; } } $totreject = $check_relay + $check_rcpt + $check_mail; $totnonrelay = $check_relay + $check_mail - $mail_errors; if ( $totreject > 0 ) { $from = "unknown"; $to = "unknown"; $from = $1 if $first =~ /^(.{7}\d\d:\d\d:\d\d )/; $to = $1 if $last =~ /^(.{7}\d\d:\d\d:\d\d )/; print "\nSendmail Rejected Messages for $from - $to\n"; print "-----------------------------------------------------------------\n"; print "$totreject rejections processed\n"; } if ($dojunk) { if ($totnonrelay > 0) { print "\n\n*** $totnonrelay Rejections by junk data file entry ***\n\n"; foreach (@junk) { &prjunk_entry($_); } &prjunk_entry("unknown"); } if ($mail_errors > 0) { print "\n\n*** $mail_errors Rejections from check_mail errors ***\n\n"; &prjunk_entry("invalid host name"); &prjunk_entry("unresolvable host name"); &prjunk_entry("illegal MAIL FROM"); } } print "\n\n*** $check_rcpt rejections by ruleset check_rcpt ***\n" if $check_rcpt > 0; if ($top > 0 && $check_rcpt > 0) { print "\nTop $top check_rcpt rejections by to email address:\n\n"; %tmp = %count_arg1_rcpt; print &sortbyvalues(); print "\nTop $top check_rcpt rejections by mail relay host:\n\n"; %tmp = %count_relay_rcpt; print &sortbyvalues(); } print "\n\n*** $check_mail rejections by ruleset check_mail ***\n" if $check_mail > 0; if ($top > 0 && $check_mail > 0) { print "\nTop $top check_mail rejections by from email address:\n\n"; %tmp = %count_arg1_mail; print &sortbyvalues(); print "\nTop $top check_mail rejections by mail relay host:\n\n"; %tmp = %count_relay_mail; print &sortbyvalues(); } print "\n\n*** $check_relay rejections by ruleset check_relay ***\n" if $check_relay > 0; if ($top > 0 && $check_relay > 0) { print "\nTop $top check_relay rejections by mail relay host:\n\n"; %tmp = %count_arg2_relay; print &sortbyvalues(); } if ($mailto) { select($real_stdout); close(MAILIT); } sub sortbyvalues { local(@data); local(@datakeys); while (($key, $value) = each(%tmp)) { push(@data, sprintf("%6d %-s\n", $value, $key)); push(@datakeys, sprintf("%6d", $value)); } sub bydatakeys { $datakeys[$a] <=> $datakeys[$b]; } @data = @data[reverse sort bydatakeys $[..$#data]; $#data = $top - 1 if ($#data >= $top); return @data; } sub findIP { local($_) = @_; foreach $junk_addr (@junk) { next if ($junk_addr =~ /[a-zA-Z]/); return $junk_addr if /^$junk_addr/; } } sub findhost { local($_) = @_; s/[<>]//g; s/.*@//; foreach $junk_addr (@junk) { next if !($junk_addr =~ /[a-zA-Z]/); next if ($junk_addr =~ /@/); return $junk_addr if /^$junk_addr$|\.$junk_addr$/i; } } sub findemail { local($_) = @_; foreach $junk_addr (@junk) { next if ! ($junk_addr =~ /[a-zA-Z]/); next if ! ($junk_addr =~ /@/); return $junk_addr if /^<$junk_addr>$/i; } } sub prjunk_entry { local($_) = @_; if ($count_junk{$_}) { printf("%6d %-s\n", $count_junk{$_}, $_) if $count_junk{$_} > 0; } }