Spam action query

Jeff A. Earickson jaearick at COLBY.EDU
Mon Jun 16 19:35:36 IST 2003


Hi,
   For what it is worth, I've just spent the last week or two writing
such a script.  Please note that the "paint is still wet" but if it
helps you, good.  Also note:

a) I'm not a full-time perl coder, so be gentle.  I also should have put
in more comments about what the code is doing.

b) It is tailored to my needs.  I use sendmail, spamassassin, RBL+,
spamhaus, and spamcop -- so those are the things I look for and count.
Suggestions to make this script more general and usable are welcome.

c) The script reads both /etc/passwd (to find out about real users),
and /etc/mail/aliases (to map alias names to real users).  Note the
hardwired stuff for domain, real_root, and real_postmaster at the
top.  Change to you needs.

d) It works when I run it interactively, but when it runs via cron
it gives me all zeroes.  Dunno why yet.  I'm looking at that.

Comments from the mailscanner crowd are welcome.  Improvements are
certainly needed to be more useful to the rest of y'all.

-----------------------------------
Jeff A. Earickson, Ph.D
Senior UNIX Sysadmin and Email Guru
Information Technology Services
Colby College, 4214 Mayflower Hill,
Waterville ME, 04901-8842
phone: 207-872-3659 (fax = 3076)
-----------------------------------

On Mon, 16 Jun 2003, Michele Neylon :: BlacknightSolutions wrote:

> Date: Mon, 16 Jun 2003 20:23:03 +0200
> From: "Michele Neylon :: BlacknightSolutions"
>     <michele at BLACKNIGHTSOLUTIONS.COM>
> Reply-To: MailScanner mailing list <MAILSCANNER at JISCMAIL.AC.UK>
> To: MAILSCANNER at JISCMAIL.AC.UK
> Subject: Spam action query
>
> Hi all
>
> I was wondering if there was some way of either generating a daily per user
> report of Spam blocked or a per message/batch per user report (though that
> would be almost as bad as the Spam we are trying to stop!)
>
> Michele
>
>
>
> #########################################################
> This message (and any attachment) is intended only for the
> recipient and may contain confidential and/or privileged
> material.  If you have received this in error, please contact the
> sender and delete this message immediately.  Disclosure, copying
> or other action taken in respect of this email or in
> reliance to it is prohibited.
>
-------------- next part --------------
#!/usr/bin/perl -w

use Carp;

$DEBUG = 0;

$Logfile = "/var/adm/syslog/0";
$Logfile = $ARGV[0] if defined $ARGV[0];

#---initialize global counters
$localdomain = "colby.edu";
$real_postmaster = "jaearick";
$real_root = "jaearick";
$limit                        = 0;
$linesread                    = 0;
$number_noqueues              = 0;
$number_gethostbyaddr         = 0;
$num_mail_msgs                = 0;
$number_single_line_mysteries = 0;
$num_incomplete_msgs          = 0;
$Total_check_rcpt             = 0;
$Relay_denied                 = 0;
$message_queued               = 0;
$message_timeout              = 0;
$reset_connections            = 0;
$Bogus_domain                 = 0;
$Unresolved                   = 0;
$User_unknown                 = 0;
$mailscanner_content_rejects  = 0;
$Msg_deleted_by_MailScanner   = 0;
$Msg_too_large                = 0;
$invalid_hostname             = 0;
$Virus_infections             = 0;
$No_mail_start                = 0;
$Msg_aborted                  = 0;
$Msg_deferred                 = 0;
$fragmented_message           = 0;
$premature_eom                = 0;
$io_error                     = 0;
$dsn_rejected                 = 0;
$syntax_error                 = 0;
$address_too_long             = 0;
$no_user_address              = 0;
$lost_input_channel           = 0;
$temp_lookup_failure          = 0;
$too_many_hops                = 0;
$unbalanced_delimiters        = 0;

$total_total                  = 0;
$total_clean                  = 0;
$total_maybe_spam             = 0;
$total_discard                = 0;
$total_mail_abuse             = 0;
$total_spamcop                = 0;
$total_spamhaus               = 0;
$total_spam_domain            = 0;
$total_spam_IP                = 0;
$total_spammer                = 0;

Read_passwd_file();
#while(($key, $value) = each %password)
#{
#	print "$key ==> $value\n";
#}
Read_Aliases_File();
#while(($key, $value) = each %alias)
#{
#	print "$key ==> $value\n";
#}

#---open the syslog file, either in gzip of plain format
if ($Logfile =~ /\.gz$/)
{
	open(LOG, "zcat $Logfile |") or die "Cannot access log file $!";
}
else
{
	open(LOG, $Logfile) or die "Cannot access log file $!";
}

#---read the logfile
while(<LOG>)
{
	$linesread++;
	$MessageID = "NULL";

	#---syslog lines spewed by sendmail
	if(/sendmail\[.*]:/)
	{
		if(/starting daemon/ || 
			/started as/ || 
			/deferring connections/ ||
			/rejecting connections/ ||
			/accepting connections/ ||
			/grew WorkList/ ||
			/alias database/ ||
			/\/etc\/mail\/aliases:/ ||
			/\/etc\/mail\/mailman.aliases:/ ||
			/POPAUTH RELAY/)
		{
			next;
		}

		#---extract the unique sendmail msg id
		$MessageID = $1 if /sendmail\[.*]: \[.*] (\S+): /;

		#---if the message was a NOQUEUE then
		#---note where the connection came from
		if($MessageID eq "NOQUEUE")
		{
			$number_noqueues++;
			#$noq = $1 if / \[(\S+)\]$/;
			#$noqueue_relay{$noq}++;

		}
		elsif($MessageID eq "gethostbyaddr")
		{
			$number_gethostbyaddr++;
		}
		#---otherwise, append all message lines
		#---together into a hash
		else
		{
			$num_mail_msgs++	if !defined $mailmsg{$MessageID};
			$num_incomplete_msgs++	if !defined $mailmsg{$MessageID};
			$mailmsg{$MessageID} .= $_;
		}

		#---if sendmail marks the message as done, then
		#---we have a complete transaction.  Analyze the
		#---message, then delete from the hash
		if(/$MessageID: done;/)
		{
			Analyze_Complete_Message($MessageID);
			delete($mailmsg{$MessageID});
			$num_incomplete_msgs--;
		}
		next;   #--- skip to the next line
	}

	#---mailscanner syslogging
	if(/MailScanner\[.*]:/)
	{
		#---mailscanner found a virus infection
		if(/INFECTED/)
		{
			$MessageID = $1 if /\.\/(\S+)\//;
			$num_mail_msgs++	if !defined $mailmsg{$MessageID};
			$num_incomplete_msgs++	if !defined $mailmsg{$MessageID};
			$mailmsg{$MessageID} .= $_;
		}
		if(/Content Checks: Detected/ ||
			/Content Checks: Fixed/)
		{
			$MessageID = $1 if /in (\S+)$/;
			$num_mail_msgs++	if !defined $mailmsg{$MessageID};
			$num_incomplete_msgs++	if !defined $mailmsg{$MessageID};
			$mailmsg{$MessageID} .= $_;
		}
		#---mailscanner made a decision about a message
		if(/Spam Actions: message/)
		{
			$MessageID = $1 if /Spam Actions: message (\S+)/;
			$num_mail_msgs++	if !defined $mailmsg{$MessageID};
			$num_incomplete_msgs++	if !defined $mailmsg{$MessageID};
			$mailmsg{$MessageID} .= $_;
		}
		#---spamassassin scored a message, add it to the
		#---sendmail message hash
		if(/SpamAssassin \(score=/)
		{
			$MessageID = $1 if /Message (\S+) from/;
			$num_mail_msgs++	if !defined $mailmsg{$MessageID};
			$num_incomplete_msgs++	if !defined $mailmsg{$MessageID};
			$mailmsg{$MessageID} .= $_;
		}
		next;   #--- skip to the next line
	}
}
close(LOG);

#---at this point we have read the logfile, analyzed all of
#---the messages that were marked as done, and now we have
#---a hash array left of those messages that didn't make it.

print "=== Lines read in the file = $linesread\n";
print "=== Number of NOQUEUEs = $number_noqueues\n";
print "=== gethostbyaddr complaints = $number_gethostbyaddr\n";
print "=== Total mail messages\t\t= $num_mail_msgs\n";
printf ("=== Complete mail messages\t= %d\n",$num_mail_msgs - $num_incomplete_msgs);
print "=== Incomplete mail messages\t= $num_incomplete_msgs\n\n";

#---analyze the remaining incomplete messages
$num_unprocessed = 0;
while(($key, $message) = each %mailmsg)
{
	$value = Analyze_Incomplete_Message($key);
	if($value == 0)
	{
		$num_unprocessed++;
	}
	delete($mailmsg{$MessageID});
}

print "\n=== unprocessed messages = $num_unprocessed\n";
print "=== check_rcpt failures = $Total_check_rcpt\n";
print "=== single-line failures = $number_single_line_mysteries\n";
print "=== Unknown users = $User_unknown\n";
print "=== RSET connections = $reset_connections\n";
print "=== Address or headers too large = $address_too_long\n";
print "=== Syntax error = $syntax_error\n";
print "=== fragmented messages rejected = $fragmented_message\n";
print "=== mailscanner rejected contents = $mailscanner_content_rejects\n";
print "=== deferred messages = $Msg_deferred\n";
print "=== DSN rejected = $dsn_rejected\n";
print "=== I/O errors = $io_error\n";
print "=== too many hops = $too_many_hops\n";
print "=== aborted messages = $Msg_aborted\n";
print "=== Premature End-of-Message = $premature_eom\n";
print "=== lost-input-channel messages = $lost_input_channel\n";
print "=== temporary lookup failures = $temp_lookup_failure\n";
print "=== invalid host/route addresses = $invalid_hostname\n";
print "=== No user address = $no_user_address\n";
print "=== queued messages = $message_queued\n";
print "=== message timeouts = $message_timeout\n";
print "=== unbalanced delimiters = $unbalanced_delimiters\n";
print "=== messages deleted by MailScanner = $Msg_deleted_by_MailScanner\n";
print "=== messages too large = $Msg_too_large\n";
print "=== with no MAIL/ETRN/EXPN/VRFY = $No_mail_start\n";
print "=== Virus Infected messages = $Virus_infections\n";

#---print noqueue relays
#foreach $k (sort keys %noqueue_relay)
#{
#	print "$k: $noqueue_relay{$k}\n";
#}

print "\n=== Total emails per user";
print " (top $limit)" if $limit;
print ":\n";
@usertotal = sort {$email_total{$b} <=> $email_total{$a}} keys(%email_total);
@usertotal = splice(@usertotal,0,$limit) if $limit;
printf("%8s:\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\n",
"UserID","TOTAL","CLEAN","MAYBE","DISCRD","MAPS","SPMCOP","SPMHUS",
"DOMAIN","IP-NUM","SPAMMER");
for $user (@usertotal)
{
	#printf("%8s: %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n",
	printf("%8s:\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\n",
	$user, $email_total{$user},
	$email_clean{$user},$email_maybe_spam{$user},
	$email_discard{$user},$email_mail_abuse{$user},
	$email_spamcop{$user},$email_spamhaus{$user},
	$email_spam_domain{$user},$email_spam_IP{$user},
	$email_spammer{$user});
}
print "---------------------------------------------------------------------\n";
printf("%8s:\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\t%-7s\n",
"Totals","TOTAL","CLEAN","MAYBE","DISCRD","MAPS","SPMCOP","SPMHUS",
"DOMAIN","IP-NUM","SPAMMER");
#printf("%8s: %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n","Totals",
printf("%8s:\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\t%5d\n",
$total_total,$total_clean,$total_maybe_spam,$total_discard,
$total_mail_abuse,$total_spamcop,$total_spamhaus,$total_spam_domain,
$total_spam_IP,$total_spammer);


sub Analyze_Complete_Message()
{
	my($ID, $maybe_spam, $line, $recipient);
	my(@lines, @fields, @whoto);
	my($spamscore, $spamaction, $mailer, $to);
	$ID = $_[0];

	if($DEBUG eq 1)
	{
		print "\n==== Routine Analyze_Complete_Message: $ID\n";
		print "$mailmsg{$ID}\n";
	}

	#---did spamassassin complain about the message?
	#---if so, what was the action?
	$maybe_spam = 0;
	$maybe_spam = 1	if $mailmsg{$ID} =~ /SpamAssassin/;
	if($maybe_spam eq 1)
	{
		$spamscore = $1			if $mailmsg{$ID} =~ /score=(\d+),/;
		$spamscore = $1+($2/10) if $mailmsg{$ID} =~ /score=(\d+)\.(\d+),/;
		$spamaction = $1		if $mailmsg{$ID} =~ /actions are (\S+)/;
		#printf("%s: score = %f, action = %s\n",$ID,$spamscore,$spamaction);
	}

	#---split the hash into an array of lines
	@lines = split /\n/, $mailmsg{$ID};
	foreach $line (@lines)
	{
		#---we are looking for "to" lines with "stat=Sent"
		#---which are deliveries to people
		if($line =~ /stat=Sent/)
		{
			#---look for the mailer= and to= info
			@fields = split /\s/, $line;
			foreach $field (@fields)
			{
				$mailer = $1 if $field =~ /mailer=(\S+),/;
				if($field =~ /to=/)
				{
					$tofield = $';
				}
			}
			$mailer =~ tr/A-Z/a-z/;

			#---skip outbound email, only count local deliveries
			next if $mailer ne "local";

			#---process individual local recipients
			#---we do not need to worry about aliases or bogus
			#---userids, because sendmail has already figured this out
			@whoto = split /,/, $tofield;
			foreach $to (@whoto)
			{
				$recipient = get_recipient($ID,$to);
				if($DEBUG eq 1)
				{
					print "$ID: maybe_spam=$maybe_spam, recipient=$recipient\n";
				}
				if($maybe_spam eq 1)
				{
					set_email_total($recipient);
					$total_maybe_spam++;
					$email_maybe_spam{$recipient}++;
				}
				else
				{
					set_email_total($recipient);
					$total_clean++;
					$email_clean{$recipient}++;
				}
			}
		}
	}
}

sub Analyze_Incomplete_Message()
{
	my($ID, $line, $domain, $to);
	my(@lines, $processed_msg);
	my(@fields, @whoto, $field, $tofield);
	$ID = $_[0];

	if($DEBUG eq 2)
	{
		print "\n==== Routine Analyze_Incomplete_Message: $ID\n";
		print "$mailmsg{$ID}\n";
	}

	#---split the hash into an array of lines
	@lines = split /\n/, $mailmsg{$ID};

	#---if there is only one line then a total mystery
	if(scalar(@lines) == 1)
	{
		$number_single_line_mysteries++;
		return 1;
	}

	#---the message was internal to the machine, eg from
	#---root or postmaster.  It consists only of a to and
	#---from line, with the to going to relay 127.0.0.1.
	if(scalar(@lines) == 2 && 
		$lines[0] =~ /from=/ &&
		$lines[1] =~ /to=/ && 
		$lines[1] =~ /mailer=relay/ &&
		$lines[1] =~ /relay=\[127.0.0.1\]/)
	{
		@fields = split /\s/, $lines[1];
		foreach $field (@fields)
		{
			if($field =~ /to=/)
			{
				$tofield = $';
			}
		}
		@whoto = split /,/, $tofield;
		foreach $to (@whoto)
		{
			$to = debracket($to,"<",">");
			$domain = domainof($to);
			if($domain eq $localdomain)
			{
				$recipient = get_recipient($ID,$to);
				set_email_total($recipient);
				$total_clean++;
				$email_clean{$recipient}++;
			}
		}
		return 1;
	}

	$processed_msg = 0;
	foreach $line (@lines)
	{
		if($line =~ /ruleset=check_rcpt/)
		{
			$Total_check_rcpt++;
			$processed_msg = 1;
			$no_such_user = 0;

			#---extract the user from the arg1 line
			@fields = split /,/, $line;
			foreach $field (@fields)
			{
				if($field =~ /arg1=/)
				{
					$tofield = $';
				}
			}
			#print "---tofield = $tofield\n";
			@whoto = split /,/, $tofield;
			foreach $to (@whoto)
			{
				$to = debracket($to,"<",">");
				$domain = domainof($to);
				if($domain eq $localdomain)
				{
					$recipient = get_recipient($ID,$to);
					if(defined $alias{$recipient})
					{
						#print "resetting $recipient to $alias{$recipient}\n";
						$recipient = $alias{$recipient};
					}
					if($recipient eq "UNKNOWN" || 
						!defined $password{$recipient})
					{
						$no_such_user = 1;
					}
				}
				else
				{
					$no_such_user = 1;
				}
			}
			if($no_such_user == 1)
			{
				#print "$ID: no such user $tofield\n";
				$User_unknown++;
				next;
			}

			if($line =~ /discard$/)
			{
				set_email_total($recipient);
				$total_discard++;
				$email_discard{$recipient}++;
			}
			elsif($line =~ /http:\/\/mail-abuse.org\/cgi-bin\/lookup/)
			{
				set_email_total($recipient);
				$total_mail_abuse++;
				$email_mail_abuse{$recipient}++;
			}
			elsif($line =~ /http:\/\/spamcop.net\/bl.shtml/)
			{
				set_email_total($recipient);
				$total_spamcop++;
				$email_spamcop{$recipient}++;
			}
			elsif($line =~ /http:\/\/www.abuse.net\/sbl.phtml/)
			{
				set_email_total($recipient);
				$total_spamhaus++;
				$email_spamhaus{$recipient}++;
			}
			elsif($line =~ /Domain banned because of SPAM/)
			{
				set_email_total($recipient);
				$total_spam_domain++;
				$email_spam_domain{$recipient}++;
			}
			elsif($line =~ /IP number banned because of SPAM/)
			{
				set_email_total($recipient);
				$total_spam_IP++;
				$email_spam_IP{$recipient}++;
			}
			elsif($line =~ /Mail from SPAMMERs rejected/)
			{
				set_email_total($recipient);
				$total_spammer++;
				$email_spammer{$recipient}++;
			}
			elsif($line =~ /Relaying denied/)
			{
				$Relay_denied++;
			}
			elsif($line =~ /Temporary lookup failure/)
			{
				$temp_lookup_failure++;
			}
			elsif($line =~ /virus/i)
			{
				$Virus_infections++;
			}
			elsif($line =~ /User address required/)
			{
				$no_user_address++;
			}
			elsif($line =~ /Domain of sender .* does not exist/)
			{
				$Bogus_domain++;
			}
			elsif($line =~ /Cannot resolve PTR record/ ||
					$line =~ /does not resolve/ ||
					$line =~ /defunct/ ||
					$line =~ /disconnected/ ||
					$line =~ /Do you mean/ ||
					$line =~ /Fix your return address/)
			{
				$Unresolved++;
			}
			else
			{
				print "unknown check_rcpt: $ID, $line\n";
			}
		}
		if($line =~ /actions are deliver,striphtml/)
		{
			$processed_msg = 1;
		}
		if($line =~ /Address .* too long/ ||
			$line =~ /headers too large/)
		{
			$address_too_long++;
			$processed_msg = 1;
		}
		if($line =~ /Content Checks: Detected/)
		{
			$mailscanner_content_rejects++;
			$processed_msg = 1;
		}
		if($line =~ /did not issue MAIL\/EXPN\/VRFY\/ETRN/)
		{
			$No_mail_start++;
			$processed_msg = 1;
		}
		if($line =~ /INFECTED/)
		{
			$Virus_infections++;
			$processed_msg = 1;
		}
		if($line =~ /invalid domain name/)
		{
			$Bogus_domain++;
			$processed_msg = 1;
		}
		if($line =~ /actions are delete/)
		{
			$Msg_deleted_by_MailScanner++;
			$processed_msg = 1;
		}
		if($line =~ /Invalid route address/ ||
			$line =~ /Invalid host name/ ||
			$line =~ /Cannot mail directly to files/ ||
			$line =~ / Hostname required/)
		{
			$invalid_hostname++;
			$processed_msg = 1;
		}
		if($line =~ /lost input channel/)
		{
			$lost_input_channel++;
			$processed_msg = 1;
		}
		if($line =~ /we do not allow DSN/)
		{
			$dsn_rejected++;
			$processed_msg = 1;
		}
		if($line =~ /premature EOM/)
		{
			$premature_eom++;
			$processed_msg = 1;
		}
		if($line =~ /stat=aborted/)
		{
			$Msg_aborted++;
			$processed_msg = 1;
		}
		if($line =~ /stat=Deferred/ ||
			$line =~ /could not send message for past . day/)
		{
			$Msg_deferred++;
			$processed_msg = 1;
		}
		if($line =~ /stat=I\/O error/)
		{
			$io_error++;
			$processed_msg = 1;
		}
		if($line =~ /stat=Message exceeds maximum fixed size/)
		{
			$Msg_too_large++;
			$processed_msg = 1;
		}
		if($line =~ /stat=RSET$/i)
		{
			$reset_connections++;
			$processed_msg = 1;
		}
		if($line =~ /stat=queued$/)
		{
			$message_queued++;
			$processed_msg = 1;
		}
		if($line =~ /stat=timeout waiting for input/)
		{
			$message_timeout++;
			$processed_msg = 1;
		}
		if($line =~ /rejected fragmented message/)
		{
			$fragmented_message++;
			$processed_msg = 1;
		}
		if($line =~ /Syntax error in mailbox address/ ||
			$line =~ /8-bit character in mailbox address/ ||
			$line =~ /syntax illegal for recipient addresses/)
		{
			$syntax_error++;
			$processed_msg = 1;
		}
		if($line =~ /Too many hops/)
		{
			$too_many_hops++;
			$processed_msg = 1;
		}
		if($line =~ /\.\.\. Unbalanced/)
		{
			$unbalanced_delimiters++;
			$processed_msg = 1;
		}
		if($line =~ /User unknown/)
		{
			$User_unknown++;
			$processed_msg = 1;
		}
	}
	if($processed_msg ne 1)
	{
		if($DEBUG eq 4)
		{
			printf("%s (%d lines): check...\n",$ID,scalar(@lines));
			print "$mailmsg{$ID}\n";
		}
		&Analyze_Complete_Message($ID);
	}
	return $processed_msg;
}

sub debracket
#---returns the string between "leftchar" and "rightchar"
#---or just the string if rightchar or leftchar don't exist
{
	my($string, $leftchar, $rightchar, $left, $right);
	$string = $_[0];
	$leftchar = $_[1];
	$rightchar = $_[2];

	$left = index($string,$leftchar);
	$right = rindex($string,$rightchar);
	#print "debracket: $left, $right, $string\n";
	if($left < 0 || $right < 0)
	{
		return $string;
	}
	else
	{
		return substr($string, $left+1, ($right-$left)-1);
	}
}

sub domainof
#---returns the domain part of "user at some.domain" or localdomain
#---if no at sign is found in the string
#---assumes that angle brackets have been removed from the arg
{
	my($user, $domain);

	if(index($_[0],"@") < 0) { return $localdomain; }
	($user, $domain) = split(/@/,$_[0],2);
	$domain = debracket($domain,"<",">");
	$domain =~ tr/A-Z/a-z/;
	return $domain;
}

sub get_recipient
#---boil an email address down to plain userid
{
	my($ID, $recipient);
	$ID = $_[0];
	$recipient = $_[1];

	if($recipient =~ /^$/)
	{
		#confess "$ID: blank recipient\n";
		$recipient = "UNKNOWN";
		return $recipient;
	}
	#---rip the angle brackets off, if any
	$recipient = debracket($recipient,"<",">");
	#---rip off the domain stuff, if any
	$recipient = userof($recipient);
	#---convert to lower case
	$recipient =~ tr/A-Z/a-z/;
	#---if there are any leading backslash pairs
	#---caused by vacation, remove them too
	$recipient = $' if $recipient =~ /\\\\/;

	#---if recipient is a blank, then report
	if($recipient =~ /^$/)
	{
		#print "$ID: unknown recipient\n";
		$recipient = "UNKNOWN";
		return $recipient;
	}

	#---if recipient is postmaster return real postmaster
	if($recipient eq "postmaster")
	{
		return $real_postmaster;
	}
	#---if recipient is root return real root
	if($recipient eq "root")
	{
		return $real_root;
	}
	return $recipient;
}

sub Read_Aliases_File
#---read and store /etc/mail/aliases
{
	my($left, $right, $user, $to, $domain);
	my(@whoto);

	open(ALIASES, "aliases") or die "Cannot access alias file $!";
	while(<ALIASES>)
	{
		chomp;
		next if /^$/;
		next if /^#/;
		next if /REDIRECT/;
		($left, $right) = split /:/, $_;
		#print "alias: $left to $right";
		if($right =~ /,/)
		{
			@whoto = split /,/, $right;
			foreach $to (@whoto)
			{
				$domain = domainof($to);
				$recipient = get_recipient("NULL",$to);
				if($domain eq $localdomain)
				{
					if(defined $password{$recipient})
					{
						#print "$left aliased to $recipient\n";
						if(defined $alias{$left})
						{
							$alias{$left} .= ",";
							$alias{$left} .= $recipient;
						}
						else
						{
							$alias{$left} = $recipient;
						}
					}
					else
					{
						if(defined $alias{$recipient})
						{
							#print "second level1: $left, $recipient\n";
							if(defined $alias{$left})
							{
								$alias{$left} .= ",";
								$alias{$left} .= $alias{$recipient};
							}
							else
							{
								$alias{$left} = $alias{$recipient};
							}
						}
						else
						{
							print "alias $recipient not in passwd\n" if $DEBUG > 0;
						}
					}
				}
			}
		}
		else
		{
			$domain = domainof($right);
			$recipient = get_recipient("NULL",$right);
			if($domain eq $localdomain)
			{
				if(defined $password{$recipient})
				{
					#print "$left aliased to $recipient\n";
					$alias{$left} = $recipient;
				}
				else
				{
					if(defined $alias{$recipient})
					{
						#print "second level2: $left, $recipient\n";
						$alias{$left} = $alias{$recipient};
					}
					else
					{
						print "alias $recipient not in passwd\n" if $DEBUG > 0;
					}
				}
			}
		}
	}
}

sub Read_passwd_file
#---read and store /etc/passwd
{
	my($userid, $pw, $uid);

	open(PASSWD, "passwd") or die "Cannot access alias file $!";
	while(<PASSWD>)
	{
		chomp;
		($userid, $pw, $uid) = split /:/, $_;
		$password{$userid} = $uid;
	}
	close(PASSWD);
}

sub set_email_total
#---if email_total has not been set for a user, then the
#---counter arrays are all initialized for that user,
#---otherwise the email_total is just incremented
{
	if(defined($email_total{$_[0]}))
	{
		$email_total{$_[0]}++;
	}
	else
	{
		$email_total{$_[0]}++;
		$email_clean{$_[0]} = 0;
		$email_maybe_spam{$_[0]} = 0;
		$email_discard{$_[0]} = 0;
		$email_mail_abuse{$_[0]} = 0;
		$email_spamcop{$_[0]} = 0;
		$email_spamhaus{$_[0]} = 0;
		$email_spam_domain{$_[0]} = 0;
		$email_spam_IP{$_[0]} = 0;
		$email_spammer{$_[0]} = 0;
	}
	$total_total++;
}

sub userof
#---returns the user part of "user at some.domain" or localdomain
#---if no at sign is found in the string
#---assumes that angle brackets have been removed from the arg
{
	my($user, $domain);

	if(index($_[0],"@") < 0) { return $_[0]; }
	($user, $domain) = split(/@/,$_[0],2);
	return $user;
}


More information about the MailScanner mailing list