On Jun 1, 2012, at 3:30 PM, Steve Kemp wrote:
> On Fri Jun 01, 2012 at 15:17:51 -0700, Matt Simerson wrote:
>
>> Introducing zombies and a reaper.
>
> Cute names..
Thanks.
>> Notice the bolded lines. Instead of rejecting the message early,
>> plugins like dnsbl and karma can zombify it. To increase efficiency,
>> other plugins detect the zombie state and skip processing.
>
> This seems to be a specific case of the general idea to lower
> overhead:
Aye.
But reducing code and simplifying plugins is just as important. This
implementation adds more control and flexibility to plugins without additional
code. For example, now it's possible (without writing code) to configure all
plugins to reject early or late by setting them all to <reject zombie>. Some
plugins already allow this ability, many do not, and all of them become simpler
with this.
> * Avoid processing messages once you've decided they are spam.
>
> Your idea is nice, and it does work well in practise if you can
> skip later plugins based on earlier ones.
And we can! It gets really simple if there is a method available to all
plugins like this one:
sub is_immune {
my $self = shift;
if ( $self->connection->notes('zombie') ) {
$self->log(LOGINFO, "skip, zombie");
return 1;
};
if ( $self->connection->notes('rejected') ) {
$self->log(LOGINFO, "skip, already rejected");
return 1;
};
if ( $self->qp->connection->relay_client() ) {
$self->log(LOGINFO, "skip, relay client");
return 1;
};
if ( $self->qp->connection->notes('whitelisthost') ) {
$self->log(LOGINFO, "skip, whitelisted host");
return 1;
};
if ( $self->qp->transaction->notes('whitelistsender') ) {
$self->log(LOGINFO, "skip, whitelisted sender");
return 1;
};
return;
};
and most plugins called it like this:
return DECLINED if $self->is_immune();
Several plugins already have their own versions of is_immune. As I've
refactored and added tests to them, I've abstracted their immunity tests into
is_immune methods within several of the plugins. Centralizing them further
simplifies a bunch of plugins.
> Especially if you skip resource-intensive ones - such as dspam, clamav, etc.
Yup. Part of the motivation for this plugin was to short circuit all the
intermediate plugins and handlers so I can feed the message to sa-learn and
dspam. Until dspam is trained, that's a very important step in training it. But
there's no gain in validating the HELO name, SPF, or DomainKeys. This plugin
and associated changes adds that flexibility while reducing the code and
complexity of the plugins.
> But a more general approach is to set a note "rejected" => 1, and
> have all plugins skip processing if that is set *except* a final
> whitelist plugin which might revert the decision.
I think moving the conditions for bypassing plugin processing into a
centralized is_immune() method as I have done is substantially more general. It
means adding only a single line of code to each plugin, and is_immune isn't
specific to any single note. It works equally well for whitelists, relay
clients, your rejects, and my zombies. Since many plugins already have code to
bypass processing for relay clients and whitelists, this solution reduces the
code and simplify the plugins as well.
> This approach is implemented here:
>
> http://www.steve.org.uk/Software/ms-lite/
I obviously like the rejected idea. I don't like the 'rejected' note name
though, because the rejected term is already overloaded. It means many
different things in different contexts within qpsmtpd. It's also somewhat
counterintuitive when a plugin is handling a connection that had already been
rejected. Oh, but wait, "rejected" doesn't really mean "rejected" any more.
Redefining terms adds complexity, which I've been eschewing.
Why hasn't your rejected idea been worked into qpsmtpd yet?
Matt