* greylist example
@ 2024-05-12 22:42 Eric Wong
0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2024-05-12 22:42 UTC (permalink / raw)
To: test
#!/usr/bin/perl -w
use v5.12;
use autodie qw(open);
use DBI;
use Fcntl qw(LOCK_EX LOCK_UN);
use IO::Handle ();
use Sys::Syslog qw(:DEFAULT);
# sample Postfix greylister using SQLite
#
# in /etc/postfix/master.cf:
#
# policy unix - n n - - spawn
# user=foo argv=/usr/bin/perl -w /path/to/greylist.perl
#
# in /etc/postfix/main.cf:
#
# smtpd_recipient_restrictions =
# ...
# reject_unauth_destination
# check_policy_service unix:private/policy
# ...
# make sure user= in master.cf line can write to this
my $db_file = '/var/lib/greylist/greylist.sqlite3';
my $delay = 60;
my ($dbh, $lockfh, $dbg, %GREY_ADDR);
my $pi_config = '/home/pi/.public-inbox/config';
if (-r $pi_config) {
open my $fh, '-|', qw(git config -z -l --includes -f), $pi_config;
local $/ = "\0";
my @l = grep /\.address\n/, <$fh>;
chomp(@l);
$GREY_ADDR{(split(/\n/, $_, 2))[1]} = undef for @l;
}
sub xflock ($$) {
until (flock($_[0], $_[1])) { return unless $!{EINTR} }
1;
}
# Demo SMTPD access policy routine. The result is an action just like
# it would be specified on the right-hand side of a Postfix access
# table. Request attributes are available via the %attr hash.
sub smtpd_access_policy ($) {
my ($attr) = @_;
my $recipient = lc $attr->{recipient};
return 'dunno' unless exists $GREY_ADDR{$recipient};
state $get = $dbh->prepare(<<'');
SELECT expires FROM greystate WHERE key = ?
state $upd = $dbh->prepare(<<'');
INSERT OR REPLACE INTO greystate (key,expires) VALUES (?,?)
state $clean = $dbh->prepare(<<'');
DELETE FROM greystate WHERE expires < ?
my $key = $attr->{client_address};
$key =~ s/\.[0-9]+\z// or $key =~ s/:[0-9a-f]+\z//i;
$key .= '|'.lc($attr->{sender});
$get->bind_param(1, $key);
xflock $lockfh, LOCK_EX;
$dbh->begin_work;
$get->execute;
my ($ok_at) = $get->fetchrow_array;
if (defined $ok_at) {
$dbh->rollback;
xflock $lockfh, LOCK_UN;
time >= $ok_at ? 'dunno' : 'defer_if_permit still greylisted';
} else {
my $now = time;
$upd->bind_param(1, $key);
$upd->bind_param(2, $now + $delay);
$clean->bind_param(1, $now - 86400 * 31);
$clean->execute;
$upd->execute;
$dbh->commit;
xflock $lockfh, LOCK_UN;
syslog 'info', "greylisted $key";
'defer_if_permit Greylisted';
}
}
openlog 'greylist', 'pid', 'mail';
$| = 1;
open $dbg, '>>', "/tmp/dbg.$<.greylist";
my $pid = $$;
if ($dbg) {
$dbg->autoflush;
open STDERR, '>&', $dbg;
print $dbg "$pid | $_\n" for (sort keys %GREY_ADDR);
}
open $lockfh, '>>', "$db_file.grey-flock";
$dbh = DBI->connect("dbi:SQLite:dbname=$db_file", '', '', {
AutoCommit => 1,
RaiseError => 1,
PrintError => 0,
sqlite_use_immediate_transaction => 1,
sqlite_see_if_its_a_number => 1,
}) or die "failed to open $db_file: $!";
$dbh->do('PRAGMA journal_mode = WAL');
$dbh->sqlite_busy_timeout(60_000); # slow disk :<
xflock $lockfh, LOCK_EX;
$dbh->do(<<'EOS');
CREATE TABLE IF NOT EXISTS greystate (
key TEXT PRIMARY KEY NOT NULL,
expires INTEGER NOT NULL,
UNIQUE (key)
)
EOS
xflock $lockfh, LOCK_UN;
my ($k, $v, $attr, $action);
while (<STDIN>) {
if (/([^=]+)=(.*)\n/) {
$k = substr($1, 0, 512);
$v = substr($2, 0, 512);
$attr->{$k} = $v;
print $dbg "$pid < $k=$v\n" if $dbg;
} elsif ($_ eq "\n") {
$action = smtpd_access_policy $attr;
print $dbg "$pid > $action\n" if $dbg;
print 'action='.$action."\n\n";
%$attr = ();
} else {
chop;
syslog 'error', 'unhandled garbage: %.100s', $_;
}
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2024-05-12 22:42 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-12 22:42 greylist example Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).