=head1 NAME denysoft_client_stats =head1 DESCRIPTION Plugin to randomly generate temporary failures based on past behaviour of the client. Currently the number of successful deliveries and permanent failures for the client is used to determine the probability of a rejection. =head1 CONFIG The following parameters can be passed to denysoft_client_stats: =over 4 =item per_recipient Per recipient flag - if this is set, the plugin is only run for recipients which have recipient_option 'denysoft_client_stats' set. This can be set by the aliases plugin. Default: 0. =item at_connect If this flag is set, the temporary failure will be generated immediately after the connect and the connection will be terminated. Otherwise, it will be generated in response to RCPT TO commands. Dropping the connection immediately saves resources and prevents address harvesting. On the other hand, it cannot be (de)activated per recipient. Default: 0 =item success_bias Constant to add to the number of successful deliveries in the computation of the failure probability. The ratio between success_bias and permfail_bias determines whether clients with insufficient data will be treated friendly or hostile, and the absolute value determines the weight of the collected data. Default: 1 =item permfail_bias Constant to add to the number of permanent failures in the computation of the failure probability. Default: 1 =back =head1 NOTES This plugin makes use of the following connection notes: =over =item 'client_stats' Contains the stats for the connected client at the beginning of this connection. Note that if the client is unknown, the stats are taken from the smallest surrounding network for which stats are known. Thus a new zombie from a network which has already a lot of zombies gets a high probability of rejection. =item 'client_options'->{denysoft_client_stats}{skip} If true, do nothing. This note is usually set by the client_options plugin. =back and of the following transaction notes: =over =item 'sender_options'->{denysoft_client_stats}{skip} If true, do nothing. There is currently no plugin which sets this option, but it is intended for whitelisting based on the reverse path. =item 'recipient_options'->{denysoft_client_stats} If true, generate random temporary failures based on past behaviour of the client. This is only used if the per_recipient flag is set. This note is usually set by the aliases plugin. =back =head1 BUGS =head1 COPYRIGHT AND LICENSE Copyright (c) 2004-2006 Peter J. Holzer This plugin is licensed under the same terms as the qpsmtpd package itself. Please see the LICENSE file included with qpsmtpd for details. =cut use Data::Dumper; sub register { my ($self, $qp, %arg) = @_; $self->{_client_stats_per_recipient} = $arg{per_recipient}; $self->{_client_stats_success_bias} = $arg{success_bias} || 1; $self->{_client_stats_permfail_bias} = $arg{permfail_bias} || 1; if ($arg{at_connect}) { $self->register_hook("connect", "handler"); } else { $self->register_hook("rcpt", "handler"); } } sub handler { my ($self, $transaction) = @_; if ($self->{_client_stats_per_recipient}) { my $ro = $transaction->notes('recipient_options'); return DECLINED unless ($ro && $ro->{denysoft_client_stats}); } my $s = $self->qp->connection->notes('client_stats'); my $m = $s->{mostspecific}; my $p = ($m->{permfail} + $self->{_client_stats_permfail_bias}) / ($m->{permfail} + $self->{_client_stats_permfail_bias} + $m->{success} + $self->{_client_stats_success_bias}); my $client = $m->{ip1}.".".$m->{ip2}.".".$m->{ip3}.".".$m->{ip4}."/".$m->{mask}; if (rand() < $p) { $self->log(LOGINFO, "$m->{permfail} permanent failures, $m->{success} successful deliveries from $client: Rejecting with probability $p"); return (DENYSOFT, "$m->{permfail} permanent failures, $m->{success} successful deliveries from $client: Rejecting with probability $p"); } else { $self->log(LOGINFO, "$m->{permfail} permanent failures, $m->{success} successful deliveries from $client: Passing"); return (DECLINED); } }