Browse Source

new package: pb4sd 1.0 (POP-before-SMTP Daemon)

master
parent
commit
83023f8ead
  1. 268
      pb4sd/pb4sd
  2. 81
      pb4sd/pb4sd.spec
  3. 47
      pb4sd/rc.pb4sd

268
pb4sd/pb4sd

@ -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

81
pb4sd/pb4sd.spec

@ -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

47
pb4sd/rc.pb4sd

@ -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…
Cancel
Save