File Coverage

blib/lib/Mail/MtPolicyd/Plugin/DBL.pm
Criterion Covered Total %
statement 50 57 87.7
branch 16 24 66.6
condition 2 6 33.3
subroutine 8 8 100.0
pod 1 1 100.0
total 77 96 80.2


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