oreilly.comSafari Books Online.Conferences.


Using Qpsmtpd
Pages: 1, 2, 3, 4

Useful Plugins

Qpsmtpd comes with a wealth of useful plugins. Rather than go into great detail on each, here's a synopsis of some of the more useful ones.

  • check_earlytalker--This plugin can reduce your spam load by up to 50 percent on its own, with zero false positives. It does this by watching for spam zombies that talk before they should in the SMTP session. I recommend a wait option of anywhere between 8 and 25 seconds to be most effective, though beware that it will cause this much delay for each and every connection, potentially increasing your concurrency.
  • check_spamhelo--This allows you to set up config/badhelo to contain known bad spam HELO strings, such as your own IP address, your own domain name, and some frequently forged and never legitimate domains such as,, and
  • dnsbl--One of qpsmtpd's strengths is the ease with which you can query multiple DNS block lists. Simply edit config/dnsbl_zones to add more block lists.
  • virus/*--There are multiple antivirus plugins available. Most free software advocates recomment clamav (I use this one, and it is very good), but you can choose from a selection of commercial scanners too. Qpsmtpd allows you to run multiple antivirus scanners based on the order you place them in config/plugins.
  • ident/p0f--If you want to write a plugin based on whether the connecting machine is Win32 or Unix, you can do that by using the information provided by the p0f plugin.

Writing Your Own Plugin

Making use of all the good stuff in qpsmtpd is all well and good, but sometimes you need to go the extra mile and write your own plugin. Rather than use a dummy example, I prefer to show how to create a plugin that watches for repeatedly denied IP addresses and locally blacklists them.

For a qpsmtpd plugin, you need two things:

  • A file in the plugins/ directory
  • A line in config/plugins

The line in config/plugins is very simple; it just needs to contain the name (or relative path) of your plugin. Qpsmtpd will split anything after the name of the plugin on white space and pass it to the plugin's init() method.

The plugin itself is a file containing ordinary Perl. Save the following file as plugins/deny_repeat_offenders.

The plugin starts with some setup code. Plugins are Perl classes, but they don't need all the usual framework that you have to provide with a Perl class; qpsmtpd builds that for you. To initialize the objects of this class, you can optionally provide an init() method. I've used that here to set up some parameters to the hooks:

use NDBM_File;
use Fcntl;

sub init {
    my ($self, $qp, $filename, $threshold) = @_;
    tie my %h, 'NDBM_File', $filename, O_RDWR|O_CREAT, 0666
        or die "Unable to tie $filename: $!";
    $self->{dbm} = \%h;
    $self->{deny_threshold} = $threshold;

Here, the filename and threshold are the parameters from the line in config/plugins (shown toward the end of this section). I've used NDBM_File to store the data, but you can use any DBM system if you prefer something different. This example doesn't do any locking of the DBM file, but you should do so in a production environment, or use something that doesn't suffer from concurrent write problems such as an RDBMS.

The plugin needs to hook into the DENY phase, which gets called whenever a mail transaction is denied for any reason. There, it will increment a value in a DBM file corresponding to the IP address.

#!perl -w

sub hook_deny {
    my ($self, $transaction, $plugin, $level) = @_;
    # We're only interested in DENY or DENY_DISCONNECT
    unless ($level == DENY or $level == DENY_DISCONNECT) {
        return DECLINED;
    return DECLINED if $plugin eq $self->plugin_name;
    # continued...

Several things are going on here: first, the name of the sub is hook_deny so that qpsmtpd knows to call it during the DENY phase. There's no other setup necessary for this sub to get called. Second, it collects the arguments, which include the plugin object itself, the transaction, a Qpsmtpd::Transaction object, the plugin that caused the DENY, and the actual code used.

Pages: 1, 2, 3, 4

Next Pagearrow

Sponsored by: