Merge pull request #202 from jaredj/greylist-storage-opts
Make Redis optional
This commit is contained in:
commit
3ed568f9d4
@ -104,6 +104,13 @@ usable directory from the following list will be used:
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
=head2 redis <host[:port]>
|
||||||
|
|
||||||
|
Location of redis server where the greylisting DB will be stored.
|
||||||
|
|
||||||
|
Redis can be used as a scalable and clusterable alternative
|
||||||
|
to a simple DBM file. For more information, see http://redis.io
|
||||||
|
|
||||||
=head2 per_recipient <bool>
|
=head2 per_recipient <bool>
|
||||||
|
|
||||||
Flag to indicate whether to use per-recipient configs.
|
Flag to indicate whether to use per-recipient configs.
|
||||||
@ -172,7 +179,7 @@ my $VERSION = '0.12';
|
|||||||
|
|
||||||
my $DENYMSG = "This mail is temporarily denied";
|
my $DENYMSG = "This mail is temporarily denied";
|
||||||
my %PERMITTED_ARGS = map { $_ => 1 } qw(per_recipient remote_ip sender
|
my %PERMITTED_ARGS = map { $_ => 1 } qw(per_recipient remote_ip sender
|
||||||
recipient black_timeout white_timeout deny_late db_dir
|
recipient black_timeout white_timeout deny_late db_dir redis
|
||||||
nfslock p0f reject loglevel geoip upgrade );
|
nfslock p0f reject loglevel geoip upgrade );
|
||||||
$PERMITTED_ARGS{grey_timeout} = 1; # Legacy argument now ignored
|
$PERMITTED_ARGS{grey_timeout} = 1; # Legacy argument now ignored
|
||||||
|
|
||||||
@ -204,13 +211,8 @@ sub register {
|
|||||||
$config->{reject} = $config->{mode} =~ /testonly|off/i ? 0 : 1;
|
$config->{reject} = $config->{mode} =~ /testonly|off/i ? 0 : 1;
|
||||||
}
|
}
|
||||||
$self->{_args} = $config;
|
$self->{_args} = $config;
|
||||||
unless ($config->{recipient} || $config->{per_recipient}) {
|
$self->init_db() or return;
|
||||||
$self->register_hook('mail', 'mail_handler');
|
$self->register_hooks();
|
||||||
}
|
|
||||||
else {
|
|
||||||
$self->register_hook('rcpt', 'rcpt_handler');
|
|
||||||
}
|
|
||||||
$self->init_db();
|
|
||||||
$self->prune_db();
|
$self->prune_db();
|
||||||
if ($self->{_args}{upgrade}) {
|
if ($self->{_args}{upgrade}) {
|
||||||
$self->convert_db();
|
$self->convert_db();
|
||||||
@ -218,23 +220,64 @@ sub register {
|
|||||||
$self->load_exclude_files();
|
$self->load_exclude_files();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub register_hooks {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->register_hook('data', 'data_handler');
|
||||||
|
if ($self->{_args}{recipient} || $self->{_args}{per_recipient}) {
|
||||||
|
$self->register_hook('rcpt', 'rcpt_handler');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->register_hook('mail', 'mail_handler');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub init_db {
|
sub init_db {
|
||||||
my ( $self ) = @_;
|
my ($self) = @_;
|
||||||
$self->db( name => 'greylist' );
|
return $self->init_redis if $self->{_args}{redis};
|
||||||
return if ! $self->db->can('path');
|
return $self->init_dbm;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub init_redis {
|
||||||
|
my ($self) = @_;
|
||||||
|
eval {
|
||||||
|
$self->db(
|
||||||
|
name => 'greylist',
|
||||||
|
class => 'Qpsmtpd::DB::Redis',
|
||||||
|
server => $self->parse_redis_server,
|
||||||
|
) or die 'Unknown error';
|
||||||
|
};
|
||||||
|
return 1 if ! $@;
|
||||||
|
$self->log(LOGCRIT, "Unable to connect to redis, GREYLISTING DISABLED: $@");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub parse_redis_server {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $server = $self->{_args}{redis};
|
||||||
|
return $server if $server =~ /:/;
|
||||||
|
return "$server:6379";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub init_dbm {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->db(
|
||||||
|
name => 'greylist',
|
||||||
|
class => 'Qpsmtpd::DB::File::DBM'
|
||||||
|
) or return 0;
|
||||||
my $cdir = $self->{_args}{db_dir};
|
my $cdir = $self->{_args}{db_dir};
|
||||||
$cdir = $1 if $cdir and $cdir =~ m{^([-a-zA-Z0-9./_]+)$};
|
$cdir = $1 if $cdir and $cdir =~ m{^([-a-zA-Z0-9./_]+)$};
|
||||||
# greylisting-specific hints for where to store the greylist DB
|
# greylisting-specific hints for where to store the greylist DB
|
||||||
my $db_dir = $self->db->dir( $cdir, '/var/lib/qpsmtpd/greylisting' );
|
my $db_dir = $self->db->dir( $cdir, '/var/lib/qpsmtpd/greylisting' );
|
||||||
|
|
||||||
return if $self->db->file_extension ne '.dbm';
|
return 1 if $self->db->file_extension ne '.dbm';
|
||||||
$self->db->nfs_locking( $self->{_args}{nfslock} );
|
$self->db->nfs_locking( $self->{_args}{nfslock} );
|
||||||
|
|
||||||
# Work around old DBM filename
|
# Work around old DBM filename
|
||||||
return if -f "$db_dir/greylist.dbm";
|
return 1 if -f "$db_dir/greylist.dbm";
|
||||||
my $oldname = 'denysoft_greylist';
|
my $oldname = 'denysoft_greylist';
|
||||||
return if ! -f "$db_dir/$oldname.dbm";
|
return 1 if ! -f "$db_dir/$oldname.dbm";
|
||||||
$self->db->name($oldname);
|
$self->db->name($oldname);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_exclude_files {
|
sub load_exclude_files {
|
||||||
@ -319,7 +362,7 @@ sub rcpt_handler {
|
|||||||
return DECLINED;
|
return DECLINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub hook_data {
|
sub data_handler {
|
||||||
my ($self, $transaction) = @_;
|
my ($self, $transaction) = @_;
|
||||||
return DECLINED unless $transaction->notes('greylist');
|
return DECLINED unless $transaction->notes('greylist');
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ sub register_tests {
|
|||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->register_test("test_load_exclude_files");
|
$self->register_test("test_load_exclude_files");
|
||||||
$self->register_test('test_hook_data');
|
$self->register_test('test_data_handler');
|
||||||
$self->register_test('test_get_greylist_key');
|
$self->register_test('test_get_greylist_key');
|
||||||
$self->register_test('test_exclude');
|
$self->register_test('test_exclude');
|
||||||
$self->register_test("test_greylist_geoip");
|
$self->register_test("test_greylist_geoip");
|
||||||
@ -22,6 +22,9 @@ sub register_tests {
|
|||||||
$self->register_test("test_greylist_p0f_uptime");
|
$self->register_test("test_greylist_p0f_uptime");
|
||||||
$self->register_test('test_exclude_file_match');
|
$self->register_test('test_exclude_file_match');
|
||||||
$self->register_test('test_greylist');
|
$self->register_test('test_greylist');
|
||||||
|
$self->register_test('test_init_redis');
|
||||||
|
$self->register_test('test_init_dbm');
|
||||||
|
$self->register_test('test_parse_redis_server');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub test_load_exclude_files {
|
sub test_load_exclude_files {
|
||||||
@ -71,27 +74,27 @@ sub test_exclude_file_match {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub test_hook_data {
|
sub test_data_handler {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $transaction = $self->qp->transaction;
|
my $transaction = $self->qp->transaction;
|
||||||
|
|
||||||
my ($code, $mess) = $self->hook_data( $transaction );
|
my ($code, $mess) = $self->data_handler( $transaction );
|
||||||
cmp_ok( $code, '==', DECLINED, "no note" );
|
cmp_ok( $code, '==', DECLINED, "no note" );
|
||||||
|
|
||||||
$transaction->notes('greylist', 1);
|
$transaction->notes('greylist', 1);
|
||||||
|
|
||||||
($code, $mess) = $self->hook_data( $transaction );
|
($code, $mess) = $self->data_handler( $transaction );
|
||||||
cmp_ok( $code, '==', DECLINED, "no recipients");
|
cmp_ok( $code, '==', DECLINED, "no recipients");
|
||||||
|
|
||||||
my $address = Qpsmtpd::Address->new( "<$test_email>" );
|
my $address = Qpsmtpd::Address->new( "<$test_email>" );
|
||||||
$transaction->recipients( $address );
|
$transaction->recipients( $address );
|
||||||
|
|
||||||
$transaction->notes('whitelistrcpt', 2);
|
$transaction->notes('whitelistrcpt', 2);
|
||||||
($code, $mess) = $self->hook_data( $transaction );
|
($code, $mess) = $self->data_handler( $transaction );
|
||||||
cmp_ok( $code, '==', DENYSOFT, "missing recipients");
|
cmp_ok( $code, '==', DENYSOFT, "missing recipients");
|
||||||
|
|
||||||
$transaction->notes('whitelistrcpt', 1);
|
$transaction->notes('whitelistrcpt', 1);
|
||||||
($code, $mess) = $self->hook_data( $transaction );
|
($code, $mess) = $self->data_handler( $transaction );
|
||||||
cmp_ok( $code, '==', DECLINED, "missing recipients");
|
cmp_ok( $code, '==', DECLINED, "missing recipients");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,3 +282,31 @@ sub rc {
|
|||||||
return return_code($r) . ": $msg";
|
return return_code($r) . ": $msg";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub test_init_redis {
|
||||||
|
my ($self) = @_;
|
||||||
|
delete $self->{db};
|
||||||
|
$self->{_args}{redis} = 'bogusserverasdfqwerty.:6379';
|
||||||
|
ok( ! $self->init_redis, 'init_redis() fails on bogus server' );
|
||||||
|
eval { Qpsmtpd::DB::Redis->new };
|
||||||
|
return if $@;
|
||||||
|
$self->{_args}{redis} = 'localhost';
|
||||||
|
ok( $self->init_redis, 'init_redis() succeeds when redis is up' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub test_init_dbm {
|
||||||
|
my ($self) = @_;
|
||||||
|
delete $self->{db};
|
||||||
|
delete $self->{_args}{redis};
|
||||||
|
ok( $self->init_db, 'init_db() works for DBM' );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub test_parse_redis_server {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->{_args}{redis} = 'asdf:1234';
|
||||||
|
is( $self->parse_redis_server, 'asdf:1234',
|
||||||
|
'parse_redis_server(): leave provided port alone' );
|
||||||
|
$self->{_args}{redis} = 'qwerty';
|
||||||
|
is( $self->parse_redis_server, 'qwerty:6379',
|
||||||
|
'parse_redis_server(): add default port' );
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user