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

Reply via email to