Skip greylisting when we can't talk to greylist DB

This commit is contained in:
Jared Johnson 2015-02-19 13:20:14 -06:00
parent 4c9bcc0ee4
commit 51ca3fcda4
2 changed files with 48 additions and 12 deletions

View File

@ -213,7 +213,8 @@ sub register {
$self->{_args} = $config; $self->{_args} = $config;
$self->init_db(); $self->init_db();
$self->register_hooks(); $self->register_hooks();
$self->prune_db(); eval { $self->prune_db(); };
$self->log(LOGERROR, "Unable to prune greylist DB: $@") if $@;
if ($self->{_args}{upgrade}) { if ($self->{_args}{upgrade}) {
$self->convert_db(); $self->convert_db();
} }
@ -383,16 +384,25 @@ sub greylist {
my $key = $self->get_greylist_key($sender, $rcpt) or return DECLINED; my $key = $self->get_greylist_key($sender, $rcpt) or return DECLINED;
my @result;
eval {
$self->db->lock;
@result = $self->greylist_result($key, $config);
$self->db->unlock;
};
return $self->errcode($@) if $@;
return @result;
}
sub greylist_result {
my ($self, $key, $config) = @_;
my $fmt = "%s:%d:%d:%d"; my $fmt = "%s:%d:%d:%d";
$self->db->lock or return DECLINED;
my $value = $self->db->get($key); my $value = $self->db->get($key);
if ( ! $value ) { if ( ! $value ) {
# new IP or entry timed out - record new # new IP or entry timed out - record new
$self->db->set( $key, sprintf $fmt, $self->now, 1, 0, 0 ); $self->db->set( $key, sprintf $fmt, $self->now, 1, 0, 0 );
$self->log(LOGWARN, "fail: initial DENYSOFT, unknown"); $self->log(LOGWARN, "fail: initial DENYSOFT, unknown");
return $self->cleanup_and_return(); return $self->failcode();
} }
my ( $ts, $new, $black, $white ) = split /:/, $value; my ( $ts, $new, $black, $white ) = split /:/, $value;
@ -404,7 +414,7 @@ sub greylist {
if ( $self->now - $ts < $config->{white_timeout} ) { if ( $self->now - $ts < $config->{white_timeout} ) {
$self->db->set( $key, sprintf $fmt, $self->now, $new, $black, ++$white ); $self->db->set( $key, sprintf $fmt, $self->now, $new, $black, ++$white );
$self->log(LOGINFO, "pass: white, $white deliveries"); $self->log(LOGINFO, "pass: white, $white deliveries");
return $self->cleanup_and_return(DECLINED); return DECLINED;
} }
else { else {
$self->log(LOGINFO, "key $key has timed out (white)"); $self->log(LOGINFO, "key $key has timed out (white)");
@ -416,26 +426,29 @@ sub greylist {
$self->db->set( $key, sprintf $fmt, $ts, $new, ++$black, 0 ); $self->db->set( $key, sprintf $fmt, $ts, $new, ++$black, 0 );
$self->log(LOGWARN, $self->log(LOGWARN,
"fail: black DENYSOFT - $black deferred connections"); "fail: black DENYSOFT - $black deferred connections");
return $self->cleanup_and_return(); return $self->failcode();
} }
$self->log(LOGWARN, "pass: timed out (grey)"); $self->log(LOGWARN, "pass: timed out (grey)");
return $self->cleanup_and_return(DECLINED); return DECLINED;
} }
# This exists purely to be overridden for testing # This exists purely to be overridden for testing
sub now { time() } sub now { time() }
sub cleanup_and_return { sub failcode {
my ($self, $return_val) = @_; my ($self) = @_;
$self->db->unlock;
return $return_val if defined $return_val; # explicit override
return DECLINED return DECLINED
if defined $self->{_args}{reject} && !$self->{_args}{reject}; if defined $self->{_args}{reject} && !$self->{_args}{reject};
return DENYSOFT, $DENYMSG; return DENYSOFT, $DENYMSG;
} }
sub errcode {
my ($self, $err) = @_;
$self->log(LOGERROR, "UNABLE TO OBTAIN GREYLIST RESULT:$err");
return DECLINED;
}
sub get_greylist_key { sub get_greylist_key {
my $self = shift; my $self = shift;
my $sender = shift || $self->qp->transaction->sender; my $sender = shift || $self->qp->transaction->sender;

View File

@ -26,6 +26,8 @@ sub register_tests {
$self->register_test('test_init_redis'); $self->register_test('test_init_redis');
$self->register_test('test_init_dbm'); $self->register_test('test_init_dbm');
$self->register_test('test_parse_redis_server'); $self->register_test('test_parse_redis_server');
$self->register_test('test_failcode');
$self->register_test('test_errcode');
} }
sub test_load_exclude_files { sub test_load_exclude_files {
@ -365,3 +367,24 @@ sub test_parse_redis_server {
'parse_redis_server(): add default port' ); 'parse_redis_server(): add default port' );
} }
sub test_failcode {
my ($self) = @_;
$self->{_args}{reject} = 0;
is( $self->rc( $self->failcode() ), 'DECLINED',
'failcode(): reject disabled' );
delete $self->{_args}{reject};
is( $self->rc( $self->failcode() ), 'DENYSOFT: This mail is temporarily denied',
'failcode(): reject enabled' );
}
sub test_errcode {
my ($self) = @_;
delete $self->qp->{_logged};
is( $self->rc( $self->errcode('test error') ),
'DECLINED',
'errcode(): proper return code' );
is( join("\n", @{ $self->qp->{_logged} || [] }),
'LOGERROR:greylistingUNABLE TO OBTAIN GREYLIST RESULT:test error',
'errocode(): logged error' );
}