Jared Johnson wrote:
> I don't imagine that this idea 
> of having everything wait for DATA will be adopted: it limits the 
> usefulness of plugins that could have rejected earlier, and in my mind 
> it doesn't really simplify the problem much.

I'm not really suggesting that it be "adopted" in that sense.  But what
would make sense is to have each plugin operating to a common model for
whether filtering is on, deciding when to reject, logging, reason
reporting etc, where "don't reject until after DATA" could be a
configuration option, as would "reject as soon as you decide you don't
want it" (plus various marking techniques etc)

Or even to the point where each plugin where it makes sense could have a
configuration option for "reject now or later", with a global default
setting.

> We already face the 
> problem that you can't reject specific recipients after DATA;

I reject "no such recipient" as soon as I know it - _not_ after DATA.

We also have whitelisted recipients, that can turn off filtering for
individual users.

In fact, recipient validation checks are configurable on a per-received
domain basis.  Which means that the same instance/config does both "full
verification" (forwarding etc) on our official domains, and "do no
verification at all" (no forwarding) on our trap domains.

> at best 
> you can accept for all and either silently ignore the recipients that 
> you wanted to reject or else bounce for them.  That problem would be 
> amplified by making everything happen after DATA rather than allowing us 
> to reject specific recipients.

Haven't done NDRs at DATA time since the very early days of Mailshield
in 1997 ;-)

If you have the oomph for it, rejecting spam after DATA (and NDRs after
RCPT) is probably about the optimal solution.  You can quarantine
everything (except NDRs that you reject on) if you wish, and you still
don't blowback.

> I'm currently looking at the custom DSN module that we wrote in order to 
> allow easy logging of results per-recipient and to consider the action 
> taken separately from the reason the action was taken.  I'm revisiting 
> the module for our own purposes, and whil I'm at it I want to replace my 
> completely separate module with patches to Qpsmtpd::DSN that would 
> increase its ease of use while retaining backward-compatible 
> functionality.  We'll see how that goes, and we'll see whether that 
> paves the way for more changes toward per-recipient configuration etc.

At present, at rcpt-to time, we have a plugin that consults a config
table (indexed by domain, but it could also do per-user) that decides a
number of things, such as:

- do we verify recipients?
- is filtering on for this domain?
- do we forward email destined to this domain or not?
- do we save copies?

This example plugin gives a sense of what we're doing.  Some comments
added for clarity.  This is the plugin for checking for bad helos:

use NTMqplib qw(configpattern);

sub register {
    my ( $self, $qp, @args ) = @_;
    # common string pattern file reading function - handles regexp and
    # non-regexp interspersed,  All string/pattern matching plugins
    # use this.  config/<name> is read, and compiled pattern is stuffed
    # in $self->{f_<name>}
    &NTMqplib::configpattern($self, 'badhelo');
    return;
}


#       Doing this later (after MAILFROM) so the per-recipient
#       domain configuration has been done by this point, and
#       we know whether to do this or not.
sub hook_data {
  my ($self, $transaction) = @_;

  # skip if: not filtering, no config, or we already have a
  # rejection reason
  return DECLINED if $transaction->notes('filter') !~ /yes/i ||
    !$self->{f_badhelo}  ||  $transaction->notes('reason');

  my $host = $self->qp->connection->hello_host();

  # I synthesize my own sessionid.  Whatever happened to this in core?
  my $session = $transaction->notes('sessionid');
  $host = lc $host;

  # no hello?  Er, scary, not sure I trust rejecting on this
  return DECLINED if !$host;

  # check for and scan patterns:
  if ($host =~ /$self->{f_badhelo}/o) {
    # generate log message - these are a standard format so I can
    # uniformly parse logs, and generate stats on each patten for each
    # config file.  Reason ends up looking like:
    # badhelo(actual match): <text>
    # where "badhelo" is the config file, and "actual match" is a key
    # into the config file of what "hit".
    my $content =
        &NTMqplib::genreason('badhelo', $1, "Bad HELO/EHLO name $host");
    $self->log(LOGINFO, $content);
    # set rejection reason
    $transaction->notes('reason', $content);
  }

  # Here a more flexible version can decide whether to DENY or DECLINED.
  return DECLINED;
}

We've made corresponding changes to SA/ClamAV plugins etc.

Reply via email to