David Nicol wrote:
Okay then.  Anyone for collaborating on setting one up, with a unified
accessor syntax similar to what we have for "notes" ?

http://advenge.com/2009/products/downloads/qpsmtpd/plugins/verp

is a working plugin that uses DirDB persistence library to rewrite the
envelope return addresses of all traffic except traffic that is exempt
due to relaying-okay or the verp_no note getting set.

a persistent notes -- maybe "pnotes" like notes, but persistent --
would be more general and could be replaced with a backend as needed.
As for lasting between two and three weeks, well, what all is needed?

It sounds like a good idea in general. We currently keep a few persistent things in our database; in the past we've kept some in Cache::FastMmap (we later decided this was premature optimization and did away with it); we are looking at keeping some things in memcached. A universal accessor for all of these types of persistent store would make it easier to generalize out plugins like, say, greylisting, and still allow people to set up whatever sort of persistent store makes sense.

I would envision the implementation in Qpsmtpd::store(), hook_store, hook_store_get, and a convenience function in Qpsmtpd::Address::store()... how about about a fake example implementing imaginary adjustable tarpit at connect time and RCPT time based on, say, the frequency of spam for a given connecting IP, and another based on frequency of spam for a given recipient (not that this is a particularly great idea..)

hook_pre_connection {
  my ($self,$txn) = @_;
  # This would be %arg or something if I read the docs
  my $ip = $self->qp->connection->remote_ip;
  _delay_connection($self->store("ip_tarpit_seconds_$ip"));
}

sub hook_rcpt {
  my ($self,$txn,$rcpt) = @_;
  # Demonstration of a convenience method for a store keyed on address
  _delay_connection($rcpt->store('tarpit_seconds'));
}

sub hook_data_post {
  my ($self,$txn) = @_;
  my $hit;
  for my $rcpt ($txn->recipients) {
    if _scan_for_spam($txn) {
      my $old = $rcpt->store('tarpit_seconds') // 0;
      $rcpt->store('tarpit_seconds',$old + 5);
      $hit = 1;
    }
  }
  return DECLINED unless $hits;
  my $ip = $self->qp->connection->remote_ip;
  my $old = $self->store("ip_tarpit_seconds_$ip");
  $self->store("ip_tarpit_seconds_$ip",$old + 5);
}

sub hook_store {
  my ($self,$txn,$addr,$key,$value) = @_;
  if ($addr) {
    # A Qpsmtpd::Address or compatible object was passed,
    # probably because we were called with $rcpt->store()
    # I want to do funky stuff with it to build my real key
    $key = $addr->notes('uid') . '_' . $addr->format . "_$key";
  }
  eval {
    my $cache = Cache::FastMmap->new(...);
    $cache->set($key,$value);
  };
  return $@ ? DECLINED : OK;
}

sub hook_store_get {
  my ($self,$txn,$addr,$key) = @_;
  if ($addr) { ... } # juju like above
  my $cache = Cache::FastMmap->new(...);
  return OK,$cache->get($key);
}

Some things I'm not so sure about:

- Two hooks, one for retrieval and one for storage? I think that's probably the way to go - Do we always pass a possible address object to the hooks? Or do we leave it out and let people manually muck with their keys? It seems to me that it would be very useful to give people means to construct keys in interesting ways without having to expose that on the caller end; but is this the best way to do so? You could go as far as to allow *any* number of arguments, which get passed to the hook, which takes the last argument as the value and uses the rest after $self and $transaction to construct the key. That would probably a bit overboard, but maybe there are other ideas.

Thoughts?

And, just out of curiosity, where would DirDB be more appropriate to use than memcached or Cach::FastMmap?

-Jared

Reply via email to