postfix and dovecot 2.0.9 rejecting over quota user on smtp stage

i’m switching from courier-imap to dovecot recently. unfortunately on my centos 6.7, i only have dovecot 2.0.9 available. quota work as expected. the only thing that bother me, everytime someone outside world sent email to one of overquota user, dovecot create bounce mail to the sender. which is not good. expert said : “you will create backscattering mail!”.

long time ago when i was using courier-imap, i was creating simple perl daemon to check user quota and disk usage. dovecot have additional mysql table for storing user quota usage/bytes and messages.

so here how i’ve done.

perl daemon

#!/usr/bin/perl
use File::Find;
use strict;
use warnings;
use DBI;
use DBD::mysql;
use Sys::Syslog qw(:DEFAULT setlogsock);
use base qw(Net::Server::PreFork);

#
# Initalize and open syslog.
#
openlog('postfix::CHECK-SIZE::','pid','mail');

__PACKAGE__->run;
exit;

###

sub configure_hook {
        my $self = shift;

        $self->{server}->{port}     = '127.0.0.1:20028';
        $self->{server}->{user}     = 'vmail';
        $self->{server}->{group}    = 'mail';
        $self->{server}->{pid_file} = '/tmp/quota.pid';
        $self->{server}->{setsid}   = 1;
        $self->{basedir}            = "/data/vmail/example.com/";

}

### process the request
sub process_request {
        my $self = shift;
        while(my $line = <STDIN>) {
                chomp($line);
                if ($line=~/^get\s+(.+)/i) {
                        my $user = $1;
                        trim($user);
                        my $sqlsize = checksqlsize($user);
                        if (defined $sqlsize && $sqlsize == 0) {
                                print STDOUT "200 DUNNO\n";
                                #print STDOUT "sqlsize: $sqlsize\n";
                                next;
                        }
                        #print STDOUT "sqlsize: $sqlsize\n";

                        my $usrdirsize = $user;
                        $usrdirsize =~ s/\@example\.com$/\//;
                        my $dir = $self->{basedir} . $usrdirsize;
                        my $sqlusage =  checksqlusage($user);

                        if (defined $sqlusage && defined $sqlsize) {
                        syslog("info","Checking %s maildir size: define=%s, diskusage=%s", $user, $sqlsize, $sqlusage);
                                if ( $sqlusage > $sqlsize) {
                                        print STDOUT "200 REJECT $user is over quota! maildir size: define=$sqlsize, diskusage=$sqlusage\n
";
                                        next;
                                }
                        }
                }
                print STDOUT "200 DUNNO\n";
        }
}

sub trim{
        $_[0]=~s/^\s+//;
        $_[0]=~s/\s+$//;
        return;
}

sub checksqlsize {
        my $user = $_[0];
        my $sqlresult;
        trim($user);
        my $dbh = DBI->connect('DBI:mysql:postfix:localhost', 'postfix', 'yourpassword', { RaiseError => 1 });
        my $sth = $dbh->prepare(qq{SELECT quota FROM mailbox WHERE username='$user'});
        $sth->execute();
        while (my @row = $sth->fetchrow_array) {
                $sqlresult = $row[0];
        }
        $sth->finish();
        $dbh->disconnect;
        if ($sqlresult >= 0 ) {
                return $sqlresult;
        } else {
                return undef;
        }
}

sub checksqlusage {
        my $user = $_[0];
        my $sqlresult;
        trim($user);
        my $dbh = DBI->connect('DBI:mysql:postfix:localhost', 'postfix', 'yourpassword', { RaiseError => 1 });
        my $sth = $dbh->prepare(qq{SELECT bytes FROM quota2 WHERE username='$user'});
        $sth->execute();
        while (my @row = $sth->fetchrow_array) {
                $sqlresult = $row[0];
        }
        $sth->finish();
        $dbh->disconnect;
        if ($sqlresult >= 0 ) {
                return $sqlresult;
        } else {
                return undef;
        }
}

1;

postfix section

smtpd_recipient_restrictions =
        permit_sasl_authenticated,
        permit_mynetworks,
        reject_unauth_destination,
        reject_non_fqdn_sender,
        reject_non_fqdn_recipient,
        reject_unknown_recipient_domain,
        check_recipient_access proxy:tcp:[127.0.0.1]:20028,
        reject_rbl_client zen.spamhaus.org,
        reject_rbl_client bl.spamcop.net,
        reject_rbl_client dnsbl.sorbs.net,

test

# telnet localhost 20028
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get foo@example.com
200 REJECT foo@example.com is over quota! maildir size: define=102400000, diskusage=147383243

you’re good to go 🙂

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *