IPBlock question

Jeff A. Earickson jaearick at COLBY.EDU
Fri Jun 4 16:17:36 IST 2004


Hi,
   Try the attached perl script for analysis of the contents of your
IPBlock list.

Jeff Earickson
Colby College

On Fri, 4 Jun 2004, Julian Field wrote:

> Date: Fri, 4 Jun 2004 16:06:38 +0100
> From: Julian Field <mailscanner at ECS.SOTON.AC.UK>
> Reply-To: MailScanner mailing list <MAILSCANNER at JISCMAIL.AC.UK>
> To: MAILSCANNER at JISCMAIL.AC.UK
> Subject: Re: IPBlock question
>
> Strings is not a reliable way to read the contents of a DB file.
>
> At 15:09 04/06/2004, you wrote:
> >Hi All,
> >
> >        I have set up the module so entries are being added to the access.db.
> >This works fine. However the Cronjob perl script copied off the end of
> >CustomConfig.pm which runs hourly does not seem to remove old entries
> >properly.
> >
> >What happens is that if I run strings /etc/mail/access.db I get the
> >entries for hostnames and IPs added. Then if I run the IPBlock cleaner
> >and run the strings command again it will sometimes not remove the entry
> >from the access.db and sometimes remove part of the string. E.g. will
> >remove the last segment up to the last '.' of an IP or hostname. It does
> >however seem to remove them from the IPBlock.db file and log that lines
> >were removed from the access file.
> >
> >Obvious things I have tried are:
> >        1. Making sure the $Refusal line matches in the CustomConfig.pm
> > and the
> >cron script.
> >        2. I have reduced the $OneHour to 1 so it should in theory remove
> >entries one second old (just for testing but I have waited an hour and
> >tested too).
> >        3. I have rewritten the $Refusal so there are no special chars (:// in
> >a URL was taken out).
> >        4. Ran a separate file to the main access.db for testing.
> >
> >I'm running:
> >        Fedora Core 1
> >        MailScanner 4.31.6-1 rpm
> >        sendmail-8.12.11 (built from src not rpm)
> >        perl 5.8.3-16 rpm
> >
> >Incidentally it was doing this in 4.29-7 before I upgraded earlier today.
> >
> >Thanks in advance.
> >
> >Matt
> >--
> >
> >[root at mail-gw mail]# strings IPBaccess.db
> >"550 Site blocked by MailScanner due to excessive email see
> >www.sovision.com"
> >194.105.69.87
> >"550 Site blocked by MailScanner due to excessive email see
> >www.sovision.com"
> >barney.gwork.org
> >[root at mail-gw mail]# /usr/local/sbin/IPB
> >IPBedit.pl        IPBlock-clean.pl
> >[root at mail-gw mail]# /usr/local/sbin/IPBlock-clean.pl
> >[root at mail-gw mail]# Jun  4 14:41:29 mail-gw IPBlock[2551]: Deleted 2
> >entries from sendmail access database
> >
> >[root at mail-gw mail]# strings IPBaccess.db
> >"550 Site blocked by MailScanner due to excessive email see
> >www.sovision.com"
> >194.105.69.87
> >"550 Site blocked by MailScanner due to excessive email see
> >www.sovision.com"
> >barney.gwork.org
> >
> >-------------------------- MailScanner list ----------------------
> >To leave, send    leave mailscanner    to jiscmail at jiscmail.ac.uk
> >Before posting, please see the Most Asked Questions at
> >http://www.mailscanner.biz/maq/     and the archives at
> >http://www.jiscmail.ac.uk/lists/mailscanner.html
>
> --
> Julian Field
> www.MailScanner.info
> MailScanner thanks transtec Computers for their support
>
> PGP footprint: EE81 D763 3DB0 0BFD E1DC 7222 11F6 5947 1415 B654
>
> -------------------------- MailScanner list ----------------------
> To leave, send    leave mailscanner    to jiscmail at jiscmail.ac.uk
> Before posting, please see the Most Asked Questions at
> http://www.mailscanner.biz/maq/     and the archives at
> http://www.jiscmail.ac.uk/lists/mailscanner.html
>

-------------------------- MailScanner list ----------------------
To leave, send    leave mailscanner    to jiscmail at jiscmail.ac.uk
Before posting, please see the Most Asked Questions at
http://www.mailscanner.biz/maq/     and the archives at
http://www.jiscmail.ac.uk/lists/mailscanner.html
-------------- next part --------------
#!/usr/bin/perl -I/usr/lib/MailScanner

#
#   MailScanner - SMTP E-Mail Virus Scanner
#   Copyright (C) 2002  Julian Field
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   The author, Julian Field, can be contacted by email at
#      Jules at JulianField.net
#   or by paper mail at
#      Julian Field
#      Dept of Electronics & Computer Science
#      University of Southampton
#      Southampton
#      SO17 1BJ
#      United Kingdom
#

use Net::CIDR;
use FileHandle;
use Fcntl qw(:DEFAULT :flock);
use Getopt::Std;
BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File SDBM_File) }
use AnyDBM_File;

#use strict 'vars';
#use strict 'refs';
#no  strict 'subs'; # Allow bare words for parameter %'s

#---subroutine prototypes
sub sort_criterion();
sub parseargs();

my $OneHour      = 3600; # seconds
my $WhitelistFile= '/etc/MailScanner/IPBlock.conf';
my $LockFile     = '/var/spool/MailScanner/IPBlock.lock';
my $BlockDB      = '/var/spool/MailScanner/IPBlock.db';
my $AccessDB     = '/etc/mail/db/access.db';
my $Refusal      = '"550 Site blocked by MailScanner due to excessive email"';

#---parse command line to get number of lines to print
#---default is all lines
parseargs();

#---read in the IPBlock.conf file
my $LimitsH = new FileHandle;
$LimitsH->open($WhitelistFile) or die;
$counter = 0;
while(<$LimitsH>) {
  chomp;
  s/#.*$//;
  s/^\s*//g;
  s/\s*$//g;
  next if /^$/;
  ($cidr, $limit) = split;
  $cidr =~ s/\s//g;
  $limit = 0 unless defined $limit;

  my @cidrlist = undef;
  if ($cidr =~ /-/) {
    # It looks like 152.78.67.0-152.78.69.255
    @cidrlist = Net::CIDR::range2cidr($cidr);
  } elsif ($cidr =~ /\//) {
    # It looks like 152.78.0.0/16 or 152.78/16 or 152.78/255.255.0.0
    my($network, $bits, $count);
    ($network, $bits) = split(/\//, $cidr);
    $network =~ s/\.$//; # Delete any trailing dot
    $count = split(/\./, $network);
    $network .= '.0' x (4-$count); # Fill out the CIDR for Net::CIDR
    # 152.78 now looks like 152.78.0.0
    if ($bits =~ /\./) {
      # It's like 152.78.0.0/255.255.0.0
      push @cidrlist, Net::CIDR::addrandmask2cidr($network, $bits);
    } else {
      # It's like 152.78.0.0/16
      push @cidrlist, "$network/$bits";
    }
  } elsif ($cidr =~ /default/i) {
    # It is the default value used when nothing else matches
    $DefaultMaxMessagesPerHour = $limit;
  } else {
    # Must just be an IP address or look like 152.78 or 152.78.
    $cidr =~ s/\.$//; # Delete any trailing dot
    my $count = split(/\./, $cidr);
    $cidr .= '.0' x (4-$count);
    push @cidrlist, "$cidr/" . ($count*8);
  }

  # Build the map from CIDR to message limit
  foreach (@cidrlist) {
    next unless $_;
    #print STDERR "IPBlock: adding $_\n";
    $CIDRtoLimit{$_} = $limit;
    push @CIDRlist, $_;
  }
  $counter++;
}
close($LimitsH);

#
# Lock out everything else for the whole of this script
#
my $LockFileH = new FileHandle;
openlock($LockFileH, ">$LockFile");

#
# Find all the entries to be deleted from the BlockDB file.
#

#Bind to BlockDB
my(%BlockDB, %AccessDB);
tie %BlockDB, "AnyDBM_File", $BlockDB, O_RDWR, 0644
  or BailOut("Failed to open $BlockDB, it may not exist yet, $!");
  
tie %AccessDB, "AnyDBM_File", $AccessDB, O_RDWR, 0644
  or BailOut("Failed to open $AccessDB, have you got the path wrong? $!");

#  Read and print IPBlock DB
my $now = time;
my(@ips, $ip, $value, $hostname, $count, $time, $donealready, $flag);
my $countrec = 0;
my $countdel = 0;
my $countblk = 0;
print "MailScanner IP Blocking Summary\n";
printf("S %15s: %s %s\n","IP Number","mesgs/limit","Hostname");
print "--------------------------------------------------------------\n";
@ips = sort { sort_criterion() } (keys %BlockDB);
foreach $ip (@ips)
{
  ($hostname, $count, $time, $donealready) = split(/,/, $BlockDB{$ip});
  # Is it more than an hour old, or has time_t wrapped (happens in year 2036)
  #print STDERR "Examining record for $ip, $count, $time\n";
  $countrec++;
  $flag = " ";
  if ($time>$now || $now>=$time+$OneHour) {
	$flag = "-";
    $countdel++;
  }
  if ($AccessDB{$ip} eq $Refusal) {
	$flag = "*";
    $countblk++;
  }

  #---look for CIDR and print if less than print limit
  if($countrec <= $opt_n)
  {
    #---find the limit per CIDR rule for this IP
    my($cidrkey, $foundcidr, $foundit, $limit);
    $foundit = 0;
    foreach $cidrkey (@CIDRlist) {
      #print STDERR "Looking for $ip in $cidrkey\n";
      if (Net::CIDR::cidrlookup($ip, $cidrkey)) {
        #print STDERR "Found it\n";
        $foundit = 1;
        $foundcidr = $cidrkey;
        last;
      }
    }
    # If we didn't find it, use the default value
    $limit = $foundit ? $CIDRtoLimit{$foundcidr} : $DefaultMaxMessagesPerHour;
    #print STDERR "Limit of $foundcidr is $limit\n";
	$fraction = sprintf("%d/%d",$count,$limit);
  
    printf("%1s %15s: %11s %s\n",$flag,$ip,$fraction,$hostname);
  }
}
print "--------------------------------------------------------------\n";
printf("%5d DB records in IPBlock database\n",$countrec);
printf("%5d DB records scheduled for deletion, next cron job (-)\n",$countdel);
printf("%5d DB records listed in sendmail access db file (*)\n",$countblk);

# Unlock and close the DB file
untie %BlockDB;
untie %AccessDB;

unlockclose($LockFileH);

exit 0;

sub openlock {
  my($fh, $fn) = @_;

  if (open($fh, $fn)) {
    flock($fh, LOCK_EX) or die;
  } else {
    die "Died opening $fn, $!";
  }
}

sub unlockclose {
  my($fh) = @_;

  flock($fh, LOCK_UN);
  close ($fh);
}

sub BailOut {
  warn "@_, $!";
  exit 1;
}

sub parseargs()
#---parse the command line arguments
{
	$opt_n = 999999;

	getopts("n:");

	if($#ARGV ne -1)
	{
		print "Usage is: $0 [-n number]\n";
		print "\t-n is the number of lines to print out\n";
		exit 1;
	}
}

sub sort_criterion()
#---sorts according to message count, biggest to smallest
{
  my($counta, $countb);
  (undef, $counta, undef, undef) = split /,/, $BlockDB{$a};
  (undef, $countb, undef, undef) = split /,/, $BlockDB{$b};
  return($countb <=> $counta);
}


More information about the MailScanner mailing list