#!/usr/bin/perl # Quick & Dirty Debug Script for changing Scanaddr routine $logfile = "/var/log/maillog"; # POP3 daemon log. $pidfile = "/var/run/poprelayd.pid"; # Where we put our PID. $dbfile = "/etc/mail/popauth.db"; # Sendmail map to update. $dbtype = "DB_HASH"; $timeout_minutes = 240; # Minutes an entry lasts. $log_wait_interval = 5; # Number of seconds between checks # # Modules # use Getopt::Std; use Fcntl; use DB_File; use POSIX; # You may need to uncomment this if your fcntl.ph doesn't export it. sub O_EXLOCK { 0x20 }; # # Variables # undef $pid; # Process ID. undef %db; # Hash into database file. undef $lffd; # $logfile file descriptor. undef $lfino; # Inode of $logfile when we opened it. undef $lfbuf; # Buffer for data from $lffd. undef @addrs; # List of IP addresses to add. undef $lasttimeout; # Last time we did a timeout. # # Subroutines # # timeoutdb(secs) # # Remove all entries from %db more than secs seconds old. # sub timeoutdb { # Convert timeout in secs to a time_t before which we delete. my $to = time - $_[0]; foreach $key (sort(keys(%db))) { if ($db{$key} < $to) { delete $db{$key}; } } } # getlogline() # # Return the next line from $logfile, or undef if one isn't currently ready. # # XXX Note that there's a bug in this routine that causes it to ignore # blank lines. I kinda like this behaviour, so I've not fixed it. # sub getlogline { my $junk; my $ino; my $foundeof = 0; my $buf; my $count; # The first time we're called; open the logfile, skip to the end, # and remember the inode we opened. if (!defined($lffd)) { $lffd = POSIX::open($logfile, O_RDONLY|O_NONBLOCK, 0); if (!defined($lffd)) { die "Can't open $logfile\n"; } if (POSIX::lseek($lffd, 0, &POSIX::SEEK_END) == -1) { die "Can't seek to end of $logfile\n"; } ($junk, $lfino, $junk) = POSIX::fstat($lffd); } # Append new data, if available, to our buffer. $count = POSIX::read($lffd, $buf, 1024); if ($count) { $lfbuf = $lfbuf . $buf; } # Return a line, if we have one. if ($lfbuf =~ m/\n/m) { ($buf, $lfbuf) = split(/\n/m, $lfbuf, 2); return $buf; } # Check the inode number of $logfile; if it's not the saved one, # the logfile has been rotated and we need to reopen. ($junk, $ino, $junk) = POSIX::stat($logfile); if ($ino != $lfino) { POSIX::close($lf_fd); undef($lf_fd); $lffd = POSIX::open($logfile, O_RDONLY|O_NONBLOCK, 0); if (!defined($lffd)) { die "Can't open $logfile\n"; } ($junk, $lfino, $junk) = POSIX::fstat($lffd); } return undef; } # scanaddr($line) # # Scan $line to see if it's a log of a successful POP3 authentication. # Return an array of the addresses that authenticated. # sub scanaddr ($) { my $s = $_[0]; my @paddrs; # Packed IP addresses. my @addrs; # ASCII addresses. my $junk; if ($s =~ m/i(pop2|pop3|map)d\[[0-9]+\]: (Login|Auth) user=/) { # DEBUG ENTRY -- HOST NAME NO IP #$s = "Mar 7 01:48:42 intel1 imapd[1653]: Login user=kam host=intel2.peregrinehw.com"; # DEBUG ENTRY -- NO HOST NAME #$s = "Mar 7 08:18:01 intel1 ipop3d[6265]: Login user=nscainc host=[64.67.148.177] nmsgs=1/1"; # DEBUG ENTRY -- HOST NAME & IP #$s = "Mar 7 10:59:12 intel1 ipop3d[7851]: Login user=slothy host=1Cust199.tnt10.tco2.da.uu.net [63.39.89.199] nmsgs=1/1"; print "Line to clean -- $s\n"; if ($s =~ /\[\d+\.\d+\.\d+\.\d+\]/) { # IP Address with NO Host Name if ($s =~ /host=\[\d+\.\d+\.\d+\.\d+\]/) { $s =~ s/.*host=\[(.*)\].*/$1/; if ($s ne "127.0.0.1") { push (@addrs,$s); #print "IP Added to Return Stack\n"; } # Host Name with IP Address } else { $s =~ s/.*host=\S+ \[(.*)\].*/$1/; if ($s ne "127.0.0.1") { push (@addrs,$s); #print "IP Added to Return Stack\n"; } } print "IP Cleaned -- $s\n"; # Host Name with No IP Address } else { $s =~ s/.*host=(\S+).*/$1/; #print "Address Found -- $s\n"; ($junk, $junk, $junk, $junk, @paddrs) = gethostbyname($s); while (@paddrs) { $s = join('.', unpack('C4', shift(@paddrs))); if ($s ne "127.0.0.1") { push(@addrs, $s); #print "IP Added to Return Stack\n"; } } #print "Address Resolved -- $addrs[0]\n"; } return @addrs; } return (); } # cleanup # # Clean up and exit; executed on receipt of a sighup. # sub cleanup { unlink $pidfile; exit 0; } # # Main Program # $countopts = 0; # Check to see we can read/write the files we need. die "Can't read $logfile: $!\n" if ! -r $logfile; die "Can't write $dbfile: $!\n" if ! -w $dbfile; # Main loop. $lasttimeout = 0; while (1) { # Build list of addresses of recent authentications. while ($line = getlogline) { undef @ret; if (@ret = scanaddr($line)) { push(@addrs, @ret); } } sleep $log_wait_interval; }