File Coverage

blib/lib/Mail/MtPolicyd/Plugin/DBL.pm
Criterion Covered Total %
statement 50 57 87.7
branch 17 26 65.3
condition 2 6 33.3
subroutine 8 8 100.0
pod 1 1 100.0
total 78 98 79.5


line stmt bran cond sub pod time code
1             package Mail::MtPolicyd::Plugin::DBL;
2              
3 2     2   3348 use Moose;
  2         2  
  2         11  
4 2     2   8748 use namespace::autoclean;
  2         4  
  2         16  
5              
6             our $VERSION = '2.02'; # VERSION
7             # ABSTRACT: mtpolicyd plugin for checking helo,sender domain,rdns against an DBL
8              
9             extends 'Mail::MtPolicyd::Plugin';
10             with 'Mail::MtPolicyd::Plugin::Role::Scoring';
11             with 'Mail::MtPolicyd::Plugin::Role::UserConfig' => {
12             'uc_attributes' => [ 'enabled', 'sender_mode', 'helo_name_mode',
13             'reverse_client_name_mode' ],
14             };
15              
16 2     2   204 use Mail::MtPolicyd::Plugin::Result;
  2         2  
  2         35  
17              
18 2     2   949 use Mail::RBL;
  2         116814  
  2         1232  
19              
20              
21             has 'domain' => ( is => 'rw', isa => 'Str', required => 1 );
22              
23             has 'enabled' => ( is => 'rw', isa => 'Str', default => 'on' );
24              
25             has 'sender_mode' => ( is => 'rw', isa => 'Str', default => 'reject' );
26             has 'sender_score' => ( is => 'rw', isa => 'Maybe[Num]', default => 5 );
27              
28             has 'reverse_client_name_mode' => ( is => 'rw', isa => 'Str', default => 'reject' );
29             has 'reverse_client_name_score' => ( is => 'rw', isa => 'Maybe[Num]', default => 2.5 );
30              
31             has 'helo_name_mode' => ( is => 'rw', isa => 'Str', default => 'passive' );
32             has 'helo_name_score' => ( is => 'rw', isa => 'Maybe[Num]', default => 1 );
33              
34             has 'reject_message' => ( is => 'rw', isa => 'Str',
35             default => '%CHECK% rejected (%HOSTNAME%%INFO%)' );
36              
37             has '_rbl' => (
38             is => 'ro', isa => 'Mail::RBL', lazy => 1,
39             default => sub {
40             my $self = shift;
41             Mail::RBL->new($self->domain)
42             },
43             );
44              
45             sub run {
46 5     5 1 494 my ( $self, $r ) = @_;
47 5         170 my $session = $r->session;
48              
49 5         24 my $enabled = $self->get_uc( $session, 'enabled' );
50 5 100       16 if( $enabled eq 'off' ) {
51 1         5 return;
52             }
53              
54 4         10 foreach my $check ( 'sender', 'reverse_client_name', 'helo_name') {
55 9         37 my $hostname = $self->_get_hostname($r, $check);
56 9 50       20 if( ! defined $hostname ) {
57 0         0 next;
58             }
59              
60             my ( $ip_result, $info ) = $r->do_cached( $self->name.'-'.$check.'-result',
61 9     9   273 sub { $self->_rbl->check_rhsbl( $hostname ) } );
  9         250  
62 9 100       1808917 if( ! defined $ip_result ) {
63 4         203 $self->log($r, 'domain '.$hostname.' not on '.$self->domain.' blacklist');
64 4         16 next;
65             }
66              
67 5 50       314 $self->log($r, 'domain '.$hostname.' is on '.$self->domain.' blacklist'.
68             ( defined $info ? " ($info)" : '' ) );
69              
70 5         21 my $score_attr = $check.'_score';
71 5 50 33     217 if( defined $self->$score_attr &&
72             ! $r->is_already_done($self->name.'-'.$check.'-score') ) {
73 5         154 $self->add_score($r, $self->name.'-'.$check => $self->$score_attr );
74             }
75              
76 5         42 my $mode = $self->get_uc( $session, $check.'_mode' );
77 5 100       28 if( $mode eq 'reject' ) {
78 2         11 return Mail::MtPolicyd::Plugin::Result->new(
79             action => $self->_get_reject_action($check, $hostname, $info),
80             abort => 1,
81             );
82             }
83             }
84              
85 2         18 return;
86             }
87              
88             sub _get_hostname {
89 9     9   16 my ( $self, $r, $field ) = @_;
90 9         324 my $value = $r->attr($field);
91 9 50       20 if( ! defined $value ) {
92 0         0 die($field.' not defined in request!');
93             }
94             # skip unknown and empty fields
95 9 50 33     47 if( $value eq 'unknown' || $value eq '' ) {
96 0         0 return;
97             }
98             # skip ip addresses
99 9 50       31 if( $value =~ m/^\d+\.\d+\.\d+\.\d+$/) {
100 0         0 return;
101             }
102             # skip ip6 addresses
103 9 50       25 if( $value =~ m/:/) {
104 0         0 return;
105             }
106             # skip unqualified hostnames
107 9 50       30 if( $value !~ m/\./) {
108 0         0 return;
109             }
110              
111 9 100       18 if( $field eq 'sender') {
112 4         16 $value =~ s/^[^@]*@//;
113             }
114 9         17 return($value);
115             }
116              
117             sub _get_reject_action {
118 2     2   4 my ( $self, $check, $hostname, $info ) = @_;
119 2         82 my $msg = $self->reject_message;
120 2         9 $msg =~ s/%CHECK%/$check/;
121 2         9 $msg =~ s/%HOSTNAME%/$hostname/;
122 2 50       5 if( defined $info ) {
123 2         15 $msg =~ s/%INFO%/, $info/;
124             } else {
125 0         0 $msg =~ s/%INFO%//;
126             }
127              
128 2         102 return 'reject '.$msg;
129             }
130              
131             __PACKAGE__->meta->make_immutable;
132              
133             1;
134              
135             __END__
136              
137             =pod
138              
139             =encoding UTF-8
140              
141             =head1 NAME
142              
143             Mail::MtPolicyd::Plugin::DBL - mtpolicyd plugin for checking helo,sender domain,rdns against an DBL
144              
145             =head1 VERSION
146              
147             version 2.02
148              
149             =head1 DESCRIPTION
150              
151             Will check the sender, helo and reverse_client_name against an domain black list.
152              
153             =head1 PARAMETERS
154              
155             =over
156              
157             =item domain (required)
158              
159             The domain of the blacklist to query.
160              
161             =item enabled (default: on)
162              
163             Set to 'off' to disable plugin.
164              
165             Possible values: on,off
166              
167             =item uc_enabled (default: empty)
168              
169             If specified the give variable within the session will overwrite the value of 'enabled' if set.
170              
171             =item (uc_)sender_mode (default: reject), (uc_)helo_name_mode (default: passive), (uc_)reverse_client_name_mode (default: reject)
172              
173             Should the plugin return an reject if the check matches (reject) or
174             just add an score (passive).
175              
176             Possible values: reject, passive
177              
178             =item sender_score (default: 5)
179              
180             =item helo_name_score (default: 1)
181              
182             =item reverse_client_name_score (default: 2.5)
183              
184             Add the given score if check matched.
185              
186             =item score_field (default: score)
187              
188             Name of the session variable the score is stored in.
189             Could be used if multiple scores are needed.
190              
191             =back
192              
193             =head1 EXAMPLE
194              
195             Only the sender and the reverse_client_name check will cause an
196             action to be executed (mode).
197             The helo check will only add an score.
198              
199             <Plugin sh_dbl>
200             module = "RBL"
201             #enabled = "on"
202             uc_enabled = "spamhaus"
203             domain="dbl.spamhaus.org"
204              
205             # do not reject based on helo
206             #helo_name_mode=passive
207             #helo_name_score=1
208             #sender_mode=reject
209             #sender_score=5
210             #reverse_client_name_mode=reject
211             #reverse_client_name_score=2.5
212             </Plugin>
213              
214             =head1 AUTHOR
215              
216             Markus Benning <ich@markusbenning.de>
217              
218             =head1 COPYRIGHT AND LICENSE
219              
220             This software is Copyright (c) 2014 by Markus Benning <ich@markusbenning.de>.
221              
222             This is free software, licensed under:
223              
224             The GNU General Public License, Version 2, June 1991
225              
226             =cut