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   2971 use Moose;
  2         4  
  2         10  
4 2     2   8450 use namespace::autoclean;
  2         3  
  2         14  
5              
6             our $VERSION = '2.01'; # 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   189 use Mail::MtPolicyd::Plugin::Result;
  2         2  
  2         41  
17              
18 2     2   881 use Mail::RBL;
  2         114959  
  2         1240  
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 767 my ( $self, $r ) = @_;
47 5         216 my $session = $r->session;
48              
49 5         34 my $enabled = $self->get_uc( $session, 'enabled' );
50 5 100       18 if( $enabled eq 'off' ) {
51 1         6 return;
52             }
53              
54 4         11 foreach my $check ( 'sender', 'reverse_client_name', 'helo_name') {
55 9         36 my $hostname = $self->_get_hostname($r, $check);
56 9 50       23 if( ! defined $hostname ) {
57 0         0 next;
58             }
59              
60             my ( $ip_result, $info ) = $r->do_cached( $self->name.'-'.$check.'-result',
61 9     9   320 sub { $self->_rbl->check_rhsbl( $hostname ) } );
  9         354  
62 9 100       1367966 if( ! defined $ip_result ) {
63 4         230 $self->log($r, 'domain '.$hostname.' not on '.$self->domain.' blacklist');
64 4         12 next;
65             }
66              
67 5 50       458 $self->log($r, 'domain '.$hostname.' is on '.$self->domain.' blacklist'.
68             ( defined $info ? " ($info)" : '' ) );
69              
70 5         24 my $score_attr = $check.'_score';
71 5 50 33     269 if( defined $self->$score_attr &&
72             ! $r->is_already_done($self->name.'-'.$check.'-score') ) {
73 5         178 $self->add_score($r, $self->name.'-'.$check => $self->$score_attr );
74             }
75              
76 5         47 my $mode = $self->get_uc( $session, $check.'_mode' );
77 5 100       109 if( $mode eq 'reject' ) {
78 2         14 return Mail::MtPolicyd::Plugin::Result->new(
79             action => $self->_get_reject_action($check, $hostname, $info),
80             abort => 1,
81             );
82             }
83             }
84              
85 2         21 return;
86             }
87              
88             sub _get_hostname {
89 9     9   17 my ( $self, $r, $field ) = @_;
90 9         468 my $value = $r->attr($field);
91 9 50       31 if( ! defined $value ) {
92 0         0 die($field.' not defined in request!');
93             }
94             # skip unknown and empty fields
95 9 50 33     57 if( $value eq 'unknown' || $value eq '' ) {
96 0         0 return;
97             }
98             # skip ip addresses
99 9 50       39 if( $value =~ m/^\d+\.\d+\.\d+\.\d+$/) {
100 0         0 return;
101             }
102             # skip ip6 addresses
103 9 50       27 if( $value =~ m/:/) {
104 0         0 return;
105             }
106             # skip unqualified hostnames
107 9 50       33 if( $value !~ m/\./) {
108 0         0 return;
109             }
110              
111 9 100       19 if( $field eq 'sender') {
112 4         20 $value =~ s/^[^@]*@//;
113             }
114 9         25 return($value);
115             }
116              
117             sub _get_reject_action {
118 2     2   6 my ( $self, $check, $hostname, $info ) = @_;
119 2         95 my $msg = $self->reject_message;
120 2         14 $msg =~ s/%CHECK%/$check/;
121 2         10 $msg =~ s/%HOSTNAME%/$hostname/;
122 2 50       57 if( defined $info ) {
123 2         25 $msg =~ s/%INFO%/, $info/;
124             } else {
125 0         0 $msg =~ s/%INFO%//;
126             }
127              
128 2         185 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.01
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