#!/usr/bin/perl
use IO::Socket::INET;
use Sys::Syslog qw(:DEFAULT setlogsock);
# Modify these variables if needed to point to your BLD daemon
my $bld_host = "localhost";
my $bld_port = "2905";
#
# Usage: bld-postfix_policy.pl [-v]
#
# Demo delegated Postfix SMTPD policy server. This server implements
# interaction with BLD and is heavily based on example greylist.pl from
# Postfix 2.1. Logging is sent to syslogd.
#
# How it works: each time a Postfix SMTP server process is started
# it connects to the policy service socket, and Postfix runs one
# instance of this PERL script. By default, a Postfix SMTP server
# process terminates after 100 seconds of idle time, or after serving
# 100 clients. Thus, the cost of starting this PERL script is smoothed
# out over time.
#
# To run this from /etc/postfix/master.cf:
#
# bld-policy unix - n n - - spawn
# user=nobody argv=/usr/bin/perl /usr/libexec/postfix/bld-postfix_policy.pl
#
# To use this from Postfix SMTPD, use in /etc/postfix/main.cf:
#
# smtpd_recipient_restrictions =
# ...
# reject_unauth_destination
# check_policy_service unix:private/bld-policy
# ...
#
# NOTE: specify check_policy_service AFTER reject_unauth_destination
# or else your system can become an open relay.
#
# To test this script by hand, execute:
#
# % perl bld-postfix_policy.pl
#
# Each query is a bunch of attributes. Order does not matter, and
# the demo script uses only a few of all the attributes shown below:
#
# request=smtpd_access_policy
# protocol_state=RCPT
# protocol_name=SMTP
# helo_name=some.domain.tld
# queue_id=8045F2AB23
# sender=foo@bar.tld
# recipient=bar@foo.tld
# client_address=1.2.3.4
# client_name=another.domain.tld
# instance=123.456.7
# sasl_method=plain
# sasl_username=you
# sasl_sender=
# size=12345
# [empty line]
#
# The policy server script will answer in the same style, with an
# attribute list followed by a empty line:
#
# action=dunno
# [empty line]
#
#
# Syslogging options for verbose mode and for fatal errors.
# NOTE: comment out the $syslog_socktype line if syslogging does not
# work on your system.
#
$syslog_socktype = 'unix'; # inet, unix, stream, console
$syslog_facility="mail";
$syslog_options="pid";
$syslog_priority="info";
#
# BLD query submission routine. Return the action to take for the given
# IP
#
sub bld_query($$$)
{
my ($host, $port, $ip) = @_;
my $sd = IO::Socket::INET->new(PeerHost => $host, PeerPort => $port)
|| return undef;
my $action = "dunno"; # Default action
my $buf;
return $action if (sysread($sd, $buf, 1024) <= 0);
syswrite($sd, "ip?=$ip\r\n");
return $action if (sysread($sd, $buf, 1024) <= 0);
close($sd);
return "defer_if_permit Too many Users unknown from this IP" if ($buf =~ /^421 /);
return $action;
}
#
# Log an error and abort.
#
sub fatal_exit {
my($first) = shift(@_);
syslog "err", "fatal: $first", @_;
exit 1;
}
#
# This process runs as a daemon, so it can't log to a terminal. Use
# syslog so that people can actually see our messages.
#
setlogsock $syslog_socktype;
openlog $0, $syslog_options, $syslog_facility;
#
# We don't need getopt() for now.
#
while ($option = shift(@ARGV)) {
if ($option eq "-v") {
$verbose = 1;
} else {
syslog $syslog_priority, "Invalid option: %s. Usage: %s [-v]",
$option, $0;
exit 1;
}
}
#
# Unbuffer standard output.
#
select((select(STDOUT), $| = 1)[0]);
#
# Receive a bunch of attributes, evaluate the policy, send the result.
#
while (<STDIN>) {
if (/([^=]+)=(.*)\n/) {
$attr{substr($1, 0, 512)} = substr($2, 0, 512);
} elsif ($_ eq "\n") {
if ($verbose) {
for (keys %attr) {
syslog $syslog_priority, "Attribute: %s=%s", $_, $attr{$_};
}
}
fatal_exit "unrecognized request type: '%s'", $attr{request}
unless $attr{"request"} eq "smtpd_access_policy";
$action = bld_query($bld_host, $bld_port, $attr{client_address});
syslog $syslog_priority, "Action: %s", $action if $verbose;
print STDOUT "action=$action\n\n";
%attr = ();
} else {
chop;
syslog $syslog_priority, "warning: ignoring garbage: %.100s", $_;
}
}
syntax highlighted by Code2HTML, v. 0.9.1