3 changed files with 396 additions and 0 deletions
@ -0,0 +1,268 @@
|
||||
#!/usr/bin/perl |
||||
## |
||||
## pb4sd -- POP-before-SMTP Daemon |
||||
## Copyright (c) 2001 Ralf S. Engelschall <rse@engelschall.com> |
||||
## |
||||
## This program is derived from Bennett Todd <bet@rahul.net>'s |
||||
## pop-before-smtp 1.28 (http://people.oven.com/bet/pop-before-smtp/) |
||||
## |
||||
|
||||
use File::Tail; |
||||
use DB_File; |
||||
use Net::Netmask; |
||||
use Date::Parse; |
||||
use Getopt::Long; |
||||
use Fcntl ':flock'; |
||||
use POSIX qw(getpid setsid); |
||||
use IO; |
||||
|
||||
# logfile parsing patters |
||||
my $pattern = { |
||||
# QPopper 4.0.x (OpenPKG) |
||||
'qpopper' => |
||||
'^(... .. ..:..:..) (?:<\S+>|\S+) (?:/\S+?)?q?popper\S*\[\d+\]: ' . |
||||
'\([^)]*\) POP login by user "[^"]+" at \([^)]+\) (\d+.\d+.\d+.\d+)$', |
||||
# Qpopper 3.x |
||||
'popper3' => |
||||
'^(\w{3} \w{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}) \[\d+\] ' . |
||||
' Stats:\s+\w+ \d \d \d \d [\w\.]+ (\d+\.\d+\.\d+\.\d+)', |
||||
# UW ipop3d/imapd |
||||
'ipop3d' => |
||||
'^(... .. ..:..:..) \S+ (?:ipop3d|imapd)\[\d+\]: ' . |
||||
'(?:Login|Authenticated|Auth) user=\S+ host=(?:\S+)?\[(\d+\.\d+\.\d+\.\d+)\](?: nmsgs=\d+/\d+)?$', |
||||
# GNU pop3d |
||||
'popd3d' => |
||||
'^(... .. ..:..:..) \S+ gnu-pop3d\[\d+\]: ' . |
||||
'User .* logged in with mailbox .* from (\d+\.\d+\.\d+\.\d+)$', |
||||
# Cyrus |
||||
'cyrus' => |
||||
'^(... .. ..:..:..) \S+ (?:pop3d|imapd)\[\d+\]: ' . |
||||
'login: \S*\[(\d+\.\d+\.\d+\.\d+)\] \S+ \S+', |
||||
# Courier-IMAP |
||||
'courier' => |
||||
'^(... .. ..:..:..) \S+ imaplogin: ' . |
||||
'LOGIN, user=\S+, ip=\[(\d+\.\d+\.\d+\.\d+)\]$', |
||||
# Qmail pop3d |
||||
'pop3d' => |
||||
'^(... .. ..:..:..) \S+ vpopmail\[\d+\]: ' . |
||||
'vchkpw: login \[\S+\] from (\d+\.\d+\.\d+\.\d+)$', |
||||
# cucipop |
||||
'cucipop' => |
||||
'^(... .. ..:..:..) \S+ cucipop\[\d+\]: \S+ ' . |
||||
'(\d+\.\d+\.\d+\.\d+) \d+, \d+ \(\d+\), \d+ \(\d+\)', |
||||
# popa3d |
||||
'popa3d' => |
||||
'^(... .. ..:..:..) \S+ popa3d\[\d+\]: Authentication passed for \S+ -- \[(\d+.\d+.\d+.\d+)\]$', |
||||
}; |
||||
|
||||
# parameters and their defaults |
||||
my $daemon = 0; |
||||
my $infile = 'qpopper.log'; |
||||
my $dbfile = 'qpopper'; |
||||
my $popserver = 'qpopper'; |
||||
my @exclude = (); |
||||
my $grace = 1800; |
||||
my $logfile = 'pb4s.log'; |
||||
my $pidfile = 'pb4s.pid'; |
||||
|
||||
# option parsing |
||||
GetOptions( |
||||
"daemon!" => \$daemon, |
||||
"infile=s" => \$infile, |
||||
"dbfile=s" => \$dbfile, |
||||
"popserver=s" => \$popserver, |
||||
"exclude=s@" => \@exclude, |
||||
"grace=i" => \$grace, |
||||
"logfile=s" => \$logfile, |
||||
"pidfile=s" => \$pidfile, |
||||
) or die "Usage: p4bs [--daemon]\n" . |
||||
" [--infile=filename]\n" . |
||||
" [--dbfile=filename]\n" . |
||||
" [--popserver=type]\n" . |
||||
" [--exclude=a.b.c.d/x]\n" . |
||||
" [--grace=seconds]\n" . |
||||
" [--logfile=filename]\n" . |
||||
" [--pidfile=filename]\n"; |
||||
|
||||
# make sure filenames are specified as absolute paths |
||||
die "--infile requires absolute filename" if ($infile !~ m|^/|); |
||||
|
||||
# make sure it is a known pop server |
||||
die "unknown pop server '$popserver'" if (not defined($pattern->{$popserver})); |
||||
|
||||
# make sure input logfile exists |
||||
die "logfile '$infile' not found" if (not -f $infile); |
||||
|
||||
# create tail object |
||||
my $lf = File::Tail->new( |
||||
name => $infile, |
||||
maxinterval => 2, |
||||
interval => 1, |
||||
adjustafter => 3, |
||||
resetafter => 30 |
||||
) || die "unable to create tail object for '$infile'"; |
||||
|
||||
# create network block |
||||
my $nt = {}; |
||||
foreach my $exclude (@exclude) { |
||||
my $nb = new Net::Netmask ($exclude) || die; |
||||
$nb->storeNetblock($nt); |
||||
} |
||||
|
||||
# create DB hash file |
||||
my %db; |
||||
my $dbh = tie %db, 'DB_File', $dbfile, O_CREAT|O_RDWR, 0666, $DB_HASH |
||||
|| die "cannot open DB file '$dbfile': $!\n"; |
||||
|
||||
# create DB hash file descriptor |
||||
my $fd = $dbh->fd; |
||||
open(DB_FH, "+<&=$fd") || die "cannot open '$dbfile' filehandle: $!\n"; |
||||
|
||||
# delete database |
||||
flock(DB_FH, LOCK_EX) || die "(exclusive) lock failed: $!\n"; |
||||
foreach $k (keys(%db)) { |
||||
delete $db{$k}; |
||||
} |
||||
flock(DB_FH, LOCK_UN) or die "unlock failed: $!\n"; |
||||
|
||||
# open logfile |
||||
my $log = new IO::File ">>$logfile" || die; |
||||
$log->autoflush(1); |
||||
|
||||
# establish signal handlers |
||||
$SIG{__DIE__} = sub { |
||||
$log->print("[".localtime(time())."] DIE error=".join(" ", @_)."\n"); |
||||
die @_; |
||||
}; |
||||
|
||||
# start/stop logging |
||||
$log->print("[".localtime(time())."] STARTUP\n"); |
||||
$SIG{'QUIT'} = $SIG{'INT'} = $SIG{'TERM'} = sub { |
||||
$log->print("[".localtime(time())."] SHUTDOWN\n"); |
||||
exit(0); |
||||
}; |
||||
|
||||
# optionally daemonize |
||||
if ($daemon) { |
||||
my ($pid, $sess_id, $i); |
||||
|
||||
# fork and exit parent |
||||
if ($pid = fork()) { |
||||
exit(0); |
||||
} |
||||
|
||||
# detach from the terminal |
||||
$sess_id = POSIX::setsid(); |
||||
|
||||
# prevent possibility of acquiring a controling terminal |
||||
$SIG{'HUP'} = 'IGNORE'; |
||||
if ($pid = fork()) { |
||||
exit(0); |
||||
} |
||||
|
||||
# create pidfile |
||||
open(PID, ">$pidfile") || die; |
||||
printf(PID "%d\n", POSIX::getpid()); |
||||
close(PID); |
||||
|
||||
# change working directory |
||||
chdir("/"); |
||||
|
||||
# clear file creation mask |
||||
umask(0); |
||||
|
||||
# close stdio file descriptors |
||||
close(STDIN); |
||||
close(STDOUT); |
||||
close(STDERR); |
||||
|
||||
# re-open stdio file descriptors to /dev/null |
||||
open(STDIN, "+>/dev/null"); |
||||
open(STDOUT, "+>&STDIN"); |
||||
open(STDERR, "+>&STDIN"); |
||||
} |
||||
|
||||
my $t = {}; # ip to expire table |
||||
my $q = []; # ip/expire stack |
||||
while (1) { |
||||
my $line = $lf->read(); |
||||
my $now = time(); |
||||
if ($line =~ m/$pattern->{$popserver}/o) { |
||||
my ($timestamp, $ipaddr) = ($1, $2); |
||||
|
||||
# log recognition of entry |
||||
$log->print("[".localtime($now)."] SEE client=".$ipaddr. |
||||
" login=".localtime(str2time($timestamp))."\n"); |
||||
|
||||
# calculate expire time |
||||
my $expire = str2time($timestamp) || next; |
||||
$expire += $grace; |
||||
|
||||
# skip if grace period is already expired or ip is excluded |
||||
next if ($expire < $now); |
||||
next if (findNetblock($ipaddr, $nt)); |
||||
|
||||
# push ip/expire onto stack |
||||
push @{$q}, [$ipaddr, $expire]; |
||||
|
||||
# remember ip |
||||
my $already_enabled = exists($t->{$ipaddr}); |
||||
$t->{$ipaddr} = $expire; |
||||
|
||||
# skip if ip was already enabled |
||||
next if $already_enabled; |
||||
|
||||
# lock database |
||||
flock(DB_FH, LOCK_EX); |
||||
|
||||
# add entry to database |
||||
$db{$ipaddr} = "OK"; |
||||
$log->print("[".localtime($now)."] ADD client=".$ipaddr." logout=".localtime($expire)."\n"); |
||||
|
||||
# purge expired database entries |
||||
while ($q->[0][1] < $now) { |
||||
if ($q->[0][1] == $t->{$q->[0][0]}) { |
||||
$log->print("[".localtime($now)."] DEL client=".$q->[0][0]." logout=".localtime($q->[0][1])."\n"); |
||||
delete $t->{$q->[0][0]}; |
||||
delete $db{$q->[0][0]}; |
||||
} |
||||
shift @q; |
||||
} |
||||
|
||||
# synchronize database |
||||
$dbh->sync(); |
||||
|
||||
# unlock database |
||||
flock(DB_FH, LOCK_UN); |
||||
} |
||||
} |
||||
|
||||
__DATA__ |
||||
|
||||
=pod |
||||
|
||||
=head1 NAME |
||||
|
||||
pb4sd -- POP-before-SMTP Daemon |
||||
|
||||
=head1 SYNOPSIS |
||||
|
||||
B<p4bsd> |
||||
[--daemon] |
||||
[--infile=filename] |
||||
[--dbfile=filename] |
||||
[--popserver=type] |
||||
[--exclude=a.b.c.d/x] |
||||
[--grace=seconds] |
||||
[--logfile=filename] |
||||
[--pidfile=filename] |
||||
|
||||
=head1 DESCRIPTION |
||||
|
||||
B<pb4sd> is a little daemon program which watches a POP/IMAP server's |
||||
logfile for successful client authentications and writes the |
||||
corresponding IP addresses into a Berkeley-DB hash file. This hash file |
||||
then can be used by the MTA to allow relaying access. |
||||
|
||||
=cut |
||||
|
@ -0,0 +1,81 @@
|
||||
## |
||||
## pb4sd.spec -- OpenPKG RPM Specification |
||||
## Copyright (c) 2000-2001 Cable & Wireless Deutschland GmbH |
||||
## Copyright (c) 2000-2001 The OpenPKG Project <http://www.openpkg.org/> |
||||
## Copyright (c) 2000-2001 Ralf S. Engelschall <rse@engelschall.com> |
||||
## |
||||
## Permission to use, copy, modify, and distribute this software for |
||||
## any purpose with or without fee is hereby granted, provided that |
||||
## the above copyright notice and this permission notice appear in all |
||||
## copies. |
||||
## |
||||
## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
||||
## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||||
## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
||||
## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR |
||||
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
||||
## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||
## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
||||
## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
## SUCH DAMAGE. |
||||
## |
||||
|
||||
# package information |
||||
Name: pb4sd |
||||
Summary: POP-before-SMTP Daemon |
||||
URL: - |
||||
Vendor: Cable & Wireless DE |
||||
Packager: The OpenPKG Project |
||||
Distribution: OpenPKG [EXP] |
||||
Group: Mail |
||||
License: PD |
||||
Version: 1.0 |
||||
Release: 20011228 |
||||
|
||||
# list of sources |
||||
Source0: pb4sd |
||||
Source1: rc.pb4sd |
||||
|
||||
# build information |
||||
Prefix: %{l_prefix} |
||||
BuildRoot: %{l_buildroot} |
||||
BuildPreReq: OpenPKG, openpkg >= 20011227, perl, perl-db |
||||
PreReq: OpenPKG, openpkg >= 20011227, perl, perl-db, POP |
||||
AutoReq: no |
||||
AutoReqProv: no |
||||
|
||||
%description |
||||
pb4sd is a little daemon program which watches a POP/IMAP server's |
||||
logfile for successful client authentications and writes the |
||||
corresponding IP addresses into a Berkeley-DB hash file. This hash |
||||
file then can be used by the MTA to allow relaying access. |
||||
|
||||
%prep |
||||
|
||||
%build |
||||
|
||||
%install |
||||
rm -rf $RPM_BUILD_ROOT |
||||
%{l_shtool} mkdir -f -p -m 755 \ |
||||
$RPM_BUILD_ROOT%{l_prefix}/sbin \ |
||||
$RPM_BUILD_ROOT%{l_prefix}/man/man8 \ |
||||
$RPM_BUILD_ROOT%{l_prefix}/etc/rc.d \ |
||||
$RPM_BUILD_ROOT%{l_prefix}/var/pb4sd |
||||
%{l_shtool} install -c -m 755 \ |
||||
-e 's;/usr/bin/perl;%{l_prefix}/bin/perl;g' \ |
||||
%{SOURCE pb4sd} $RPM_BUILD_ROOT%{l_prefix}/sbin/ |
||||
%{l_prefix}/bin/pod2man \ |
||||
%{SOURCE pb4sd} >$RPM_BUILD_ROOT%{l_prefix}/man/man8/pb4sd.8 |
||||
%{l_shtool} install -c -m 755 \ |
||||
-e 's;@l_prefix@;%{l_prefix};g' \ |
||||
%{SOURCE rc.pb4sd} $RPM_BUILD_ROOT%{l_prefix}/etc/rc.d/ |
||||
%{l_rpmtool} files -v -ofiles -r$RPM_BUILD_ROOT %{l_files_std} |
||||
|
||||
%files -f files |
||||
|
||||
%clean |
||||
rm -rf $RPM_BUILD_ROOT |
||||
|
@ -0,0 +1,47 @@
|
||||
#!@l_prefix@/lib/openpkg/bash @l_prefix@/etc/rc |
||||
## |
||||
## rc.pb4sd -- Run-Commands for PB4SD |
||||
## |
||||
|
||||
%config |
||||
pb4sd_enable="yes" |
||||
pb4sd_dbfile="@l_prefix@/var/pb4sd/pb4sd.db" |
||||
pb4sd_logfile="@l_prefix@/var/pb4sd/pb4sd.log" |
||||
pb4sd_pidfile="@l_prefix@/var/pb4sd/pb4sd.pid" |
||||
pb4sd_grace="3600" |
||||
pb4sd_exclude="127.0.0.0/8" |
||||
pb4sd_log_prolog="true" |
||||
pb4sd_log_epilog="true" |
||||
pb4sd_log_numfiles="10" |
||||
pb4sd_log_minsize="1M" |
||||
pb4sd_log_complevel="9" |
||||
|
||||
%start -p 200 -u root |
||||
opServiceEnabled pb4sd || exit 0 |
||||
pb4sd_popserver="${pop_type:-qpopper}" |
||||
pb4sd_infile="${pop_logfile:-@l_prefix@/var/qpopper/qpopper.log}" |
||||
@l_prefix@/sbin/pb4sd \ |
||||
--daemon \ |
||||
--popserver="${pb4sd_popserver}" \ |
||||
--infile="${pb4sd_infile}" \ |
||||
--dbfile="${pb4sd_dbfile}" \ |
||||
--logfile="${pb4sd_logfile}" \ |
||||
--pidfile="${pb4sd_pidfile}" \ |
||||
--grace="${pb4sd_grace}" \ |
||||
--exclude="${pb4sd_exclude}" |
||||
|
||||
%stop -p 200 -u root |
||||
opServiceEnabled pb4sd || exit 0 |
||||
if [ -f $pb4sd_logfile ]; then |
||||
kill -TERM `cat ${pb4sd_logfile}` |
||||
fi |
||||
|
||||
%daily -u root |
||||
opServiceEnabled pb4sd || exit 0 |
||||
shtool rotate -f \ |
||||
-n${pb4sd_log_numfiles} -s${pb4sd_log_minsize} -d \ |
||||
-z${pb4sd_log_complevel} -o@l_fsusr@ -o@l_fsgrp@ -m644 \ |
||||
-P "${pb4sd_log_prolog}" \ |
||||
-E "${pb4sd_log_epilog}" \ |
||||
${pb4sd_logfile} |
||||
|
Loading…
Reference in new issue