#!/usr/bin/perl

##
# 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 n entries]
#
# -tn  list top n hosts or addresses for each ruleset.
#      n = 0 for no detail by host or email address.
#

# smreject.pl was designed to analyze rejected messages based
# on the database version of the check_* ruleset at
# http://www.informatik.uni-kiel.de/~ca/email/chk-newmap.html

# The junk file contains a combination of all of the domains,
# ip addresses, and email addresses which are to be rejected
# by sendmail. If called with no options a listing will be
# produced of each entry in the junk file and a count of the
# rejected messages triggered by each entry.  Also total counts
# of rejections by check_rcpt, check_mail, and check_relay
# rulesets are listed.

# If called with the -t option a detailed listing of the top n
# rejections by email address and mail relay host is included
# for check_rcpt and check_mail rulesets.  The top n rejections
# by mail relay host are included for the check_relay ruleset.


# 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)
#

require "getopts.pl";
&Getopts("t:");
$top = $opt_t if $opt_t;

# change these locations to match your system
# location of sendmail log file
$logfile = "/var/log/maillog";
# location of junk text file
$junk = "/etc/junk";

@junk = `cut -d" " -f1 $junk`;
chop(@junk);
open(INPUT, "$logfile") || die "Can't open $logfile.";

while (<INPUT>) {
	$first = $_ if ! $first;
	$last = $_;
	next if !/check_/;
	$ruleset = $1 if (/ruleset=([^,]+),/);

	if ($ruleset =~ /check_relay/) {
		$check_relay++;
		$arg2_relay{$check_relay} = $1 if (/arg2=([^,]+),/);
		$count_arg2_relay{$1}++;
		$junk_entry = &findIP($1);
		$count_junk{$junk_entry}++ if $junk_entry;

		$arg1_relay{$check_relay} = $1 if (/arg1=([^,]+),/);
		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++;
		$arg1_rcpt{$check_rcpt} = $1 if (/arg1=([^,]+),/);
		$count_arg1_rcpt{$1}++;
		$relay_rcpt{$check_rcpt} = $1 if (/relay=([^,]+),/);
		$count_relay_rcpt{$1}++;
	}

	if ($ruleset =~ /check_mail/) {
		$check_mail++;
		$arg1_mail{$check_mail} = $1 if (/arg1=([^,]+),/);
		$count_arg1_mail{$1}++;
		$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;
		$relay_mail{$check_mail} = $1 if (/relay=([^,]+),/);
		$count_relay_mail{$1}++;
	}
}

$totreject = $check_relay + $check_rcpt + $check_mail;
$totnonrelay = $check_relay + $check_mail - $mail_errors;
$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 ($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();
}

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;
	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($_) = @_;
	printf("%6d %-s\n", $count_junk{$_}, $_) if $count_junk{$_} > 0;
}
