[Postfixbuch-users] Dovecot-Userquota in Postfix nutzen

Gregor Hermens gregor at a-mazing.de
Di Feb 22 16:56:29 CET 2011


Am Dienstag, 22. Februar 2011 schrieb Jan Phillip Greimann:
> Am 22.02.2011 16:00, schrieb Gregor Hermens:
> > Ich habe mir dafür einen kleinen Policy-Daemon geschrieben.
> 
> Kann man diesen vielleicht zu Anschauungszwecken zeigen?

Der Code stammt zum größten Teil aus dem Beispiel-Daemon, der mit den Postfix 
sourcen geliefert wird: ~/examples/smtpd.policy/greylist.pl
und dem Policy-Readme: http://www.postfix.org/SMTPD_POLICY_README.html

Die SQL-Abfrage in Zeile 92 habe ich gekürzt, da sie spezifisch für meine 
Tabellenstruktur ist.

Gestartet wird der Daemon per Spawn aus der master.cf, eingebunden ist er in 
recipient- und end-of-data-restrictions. Beim ersten Aufrauf verlässt er sich 
auf die vom Client übermittelte Größe und kann noch einzelne Empfänger 
ablehnen, bei zweiten Aufruf ist die tatsächliche Größe bekannt, dafür müssen 
Mails mit mehr als einem Empfänger jetzt trotzdem angenommen werden. Ein paar 
wenige Bounces bleiben also erhalten.

Der Code:

policyd-quota.pl:
----------------------------------------------------------------------------
!#/usr/bin/perl

use DBI;
use Fcntl;
use Sys::Syslog qw(:DEFAULT setlogsock);

#
# Usage: policyd-quota.pl [-v]
#
# Delegated Postfix SMTPD policy server. This server implements
# quota checking against a mySQL based dovecot quota dict. Logging is
# sent to syslogd.
#
$db_type = 'mysql';
$db_host = 'localhost';
$db_user = 'dbuser';
$db_pass = 'dbpass';
$db_name = 'dbname';

#
# 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";

#
# 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($key, $free);

        if("END-OF-MESSAGE" == $attr{"protocol_state"} && 1 < 
$attr{"recipient_count"}) {
                syslog $syslog_priority, "more than one recipient in END-OF-
MESSAGE state" if $verbose;
                return "dunno";
        }

        if(0 == $attr{"size"}) {
                syslog $syslog_priority, "cannot establish size of mail for 
recipient: %s", $attr{"recipient"} if $verbose;
                return "dunno";
        }

        open_database() unless $dbh;

        $key = lc $attr{"recipient"};
        $free = read_database($key);

        if(defined $free) {
                syslog $syslog_priority, "recipient: %s, free: %d bytes, size: 
%d bytes", $attr{"recipient"}, $free, $attr{"size"} if $verbose;

                if($attr{"size"} <= $free) {
                        return "dunno";
                } else {
                        return "550 5.2.2 Mailbox full"
                }
        } else {
                syslog $syslog_priority, "cannot establish free space for 
recipient: %s", $attr{"recipient"} if $verbose;
                return "dunno";
        }
}

#
# Log an error and abort.
#
sub fatal_exit {
        my($first) = shift(@_);
        syslog "err", "fatal: $first", @_;
        exit 1;
}

#
# Open database.
#
sub open_database {
        $dbh = DBI->connect("DBI:$db_type:$db_name:$db_host", "$db_user", 
"$db_pass", { PrintError => 0, AutoCommit => 1,}) || fatal_exit "Error 
connecting to database: %s", $DBI::errstr;
}

#
# Read database.
#
sub read_database {
        my($key) = @_;
        my($value);
        my($query);

        $key = $dbh->quote($key);
        $query = "SELECT `quota`, `storage` FROM [...] WHERE `address` = $key 
[...] LIMIT 1";

        my $sth = $dbh->prepare($query) || fatal_exit "DB error: %s", $dbh-
>errstr();
        $sth->execute() || fatal_exit "DB error: %s", $dbh->errstr();
        my $row = $sth->fetchrow_hashref() || return undef;
        syslog $syslog_priority, "Database results: quota: %dk, storage: %d", 
$row->{quota}, $row->{storage} if $verbose;

        $value = $row->{quota} * 1024 - $row->{storage};
        return $value;
}


#
# 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 'policyd-quota', $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 = smtpd_access_policy();
                syslog $syslog_priority, "Action: %s; Recipient: <%s>, Size: 
%s", $action, lc $attr{"recipient"}, $attr{"size"};
                print STDOUT "action=$action\n\n";
                %attr = ();
                } else {
                chop;
                syslog $syslog_priority, "warning: ignoring garbage: %.100s", 
$_;
        }
}
-------------------------------------------------------------------------------

Gruß,
Gregor
-- 
     @mazing           fon +49 8142 6528665
  Gregor Hermens       fax +49 8142 6528669
Brucker Strasse 12  gregor.hermens at a-mazing.de
D-82216 Gernlinden    http://www.a-mazing.de/



Mehr Informationen über die Mailingliste Postfixbuch-users