Jared Johnson wrote:
I like this approach, but it's had its complications for us because we
don't want to make the compromise of doing the rest of the checks at a
given stage needlessly; e.g. if our RBL plugin hits, we don't want to
bother doing RDNS and SPF lookups only to say 'sorry, you were on an
RBL' afterward. Have you come up with an intuitive way to deal with
this sort of thing? We have come up with some ways, but I don't know
that I'd call it intuitive, yet :)
One thing we've done that dealt with the problem somewhat was to expand
the idea of the DSN object to be more customizable and to specify all
the information we need for logging, and for deciding what to do when a
particular filter hits. It hasn't really addressed the issue
completely, we've got three or four different methods in place dealing
with variations on the same problem right now...
The following general model for all my filtering plugins looks like
this. This example is looking for bad From:, using a register hook
that reads and constructs a "bad from" regexp in $self->{_badfrom}:
return DECLINED
if $transaction->notes('filter') !~ /yes/i #!filtering or
|| !$self->{f_badfrom} #no config or
|| $transaction->notes('reason'); #already zapped?
my $line = $transaction->header->get('From'); #get thing to check
if ( $line =~ /$self->{f_badfrom}/o ) { #check
my $content = &NTMqplib::genreason( 'badfrom', $1, "Bad From
$line" );
$transaction->notes( 'reason', $content );
}
return DECLINED;
[Genreason produces common format error messages.]
The model works by setting the notes "reason" for _why_ you want to
block the email, and once set, it shuts off all subsequent filtering.
You really can't get much simpler than that.
I use the exact same model for whitelisting, except that it uses a
different note.
Then, a subsequent plugin decides to DENY or not by something like this:
if ($transaction->notes{reason} && !$transaction->notes{whitelist}) {
return DENY;
}
I do all my logging by a variation of logterse, which logs reasons and
various additional things in addition to what the published logterse does.
One log line for every email that contains everything there is to know
about the email. Since the reasons are a common format, post-processing
the logs to derive reason effectiveness (right down to which entry in
which config file triggered it) is trivial.
You can design more complex systems that allow you to parameterize the
"behaviour on hit" stuff. Eg: all the stuff about rejecting, when to
reject, whether to include the reason note in the reject, quarantine,
forwarding, marking, etcetera.
What it would take would be coming up with a model, perhaps some library
support, and converting all of the plugins to it. Most plugins would
become much simpler. They generally wouldn't need their own
whitelisting mechanisms at all, and you don't have to configure their
hit behaviour either.
I strongly believe that one of qpsmtpd's biggest impediments to broad
deployment is the adhoc/inconsistent nature of the plugin suite,
particularly all the inconsistent configuration directives. Another
thing is the essentially useless logging (the released qpsmtpd doesn't
even include logterse I think).
You can't build an "enterprise level" MTA out of the current plugin
suite. You have to recode them all for consistent behaviour and
construct your own logging. The qpsmtpd _core_, on the other hand, is
just fine as is ;-)