File Coverage

blib/lib/Mail/Milter/Authentication/Handler/SenderID.pm
Criterion Covered Total %
statement 69 96 71.8
branch 21 28 75.0
condition 1 3 33.3
subroutine 16 18 88.8
pod 1 9 11.1
total 108 154 70.1


line stmt bran cond sub pod time code
1             package Mail::Milter::Authentication::Handler::SenderID;
2 25     25   12503 use strict;
  25         64  
  25         803  
3 25     25   150 use warnings;
  25         66  
  25         694  
4 25     25   131 use base 'Mail::Milter::Authentication::Handler';
  25         86  
  25         2885  
5             our $VERSION = '20191206'; # VERSION
6              
7 25     25   172 use Sys::Syslog qw{:standard :macros};
  25         66  
  25         6960  
8 25     25   186 use Mail::AuthenticationResults::Header::Entry;
  25         53  
  25         633  
9 25     25   133 use Mail::AuthenticationResults::Header::SubEntry;
  25         62  
  25         504  
10 25     25   127 use Mail::AuthenticationResults::Header::Comment;
  25         84  
  25         566  
11              
12 25     25   1137 use Mail::SPF;
  25         62  
  25         650  
13              
14 25     25   2413 use Mail::Milter::Authentication::Handler::SPF;
  25         65  
  25         17527  
15              
16             sub default_config {
17             return {
18 0     0 0 0 'hide_none' => 1,
19             };
20             }
21              
22             sub grafana_rows {
23 0     0 0 0 my ( $self ) = @_;
24 0         0 my @rows;
25 0         0 push @rows, $self->get_json( 'SenderID_metrics' );
26 0         0 return \@rows;
27             }
28              
29             sub setup_callback {
30 55     55 0 209 my ( $self ) = @_;
31             # Call connect_callback from SPF handler to setup object creation
32             # Required if SenderID is enabled but SPF is disabled.
33 55         334 return Mail::Milter::Authentication::Handler::SPF::setup_callback( $self );
34             }
35              
36             sub register_metrics {
37             return {
38 24     24 1 141 'senderid_total' => 'The number of emails processed for Sender ID',
39             };
40             }
41              
42             sub helo_callback {
43 60     60 0 251 my ( $self, $helo_host ) = @_;
44 60         216 $self->{'helo_name'} = $helo_host;
45 60         158 return;
46             }
47              
48             sub envfrom_callback {
49 60     60 0 256 my ( $self, $env_from ) = @_;
50 60 100       403 return if ( $self->is_local_ip_address() );
51 52 100       276 return if ( $self->is_trusted_ip_address() );
52 50 50       237 return if ( $self->is_authenticated() );
53 50         160 delete $self->{'from_header'};
54 50         132 return;
55             }
56              
57             sub header_callback {
58 659     659 0 1809 my ( $self, $header, $value ) = @_;
59 659 100       2304 return if ( $self->is_local_ip_address() );
60 542 100       1584 return if ( $self->is_trusted_ip_address() );
61 512 50       1438 return if ( $self->is_authenticated() );
62 512 100       1626 if ( lc $header eq 'from' ) {
63 52         169 $self->{'from_header'} = $value;
64             }
65 512         1286 return;
66             }
67              
68             sub eoh_callback {
69 60     60 0 186 my ($self) = @_;
70 60         326 my $config = $self->handler_config();
71 60 100       266 return if ( $self->is_local_ip_address() );
72 52 100       221 return if ( $self->is_trusted_ip_address() );
73 50 50       197 return if ( $self->is_authenticated() );
74              
75 50         247 my $spf_server = $self->get_object('spf_server');
76 50 50       219 if ( ! $spf_server ) {
77 0         0 $self->log_error( 'SenderID Setup Error' );
78 0         0 my $header = Mail::AuthenticationResults::Header::Entry->new()->set_key( 'senderid' )->safe_set_value( 'temperror' );
79 0         0 $self->add_auth_header($header);
80 0         0 $self->metric_count( 'senderid_total', { 'result' => 'error' } );
81 0         0 return;
82             }
83              
84 50         150 my $scope = 'pra';
85              
86 50         296 my $identity = $self->get_address_from( $self->{'from_header'} );
87              
88 50 50       214 if ( ! $identity ) {
89 0         0 $self->log_error( 'SENDERID Error No Identity' );
90 0         0 my $header = Mail::AuthenticationResults::Header::Entry->new()->set_key( 'senderid' )->safe_set_value( 'permerror' );
91 0         0 $self->add_auth_header( $header );
92 0         0 $self->metric_count( 'senderid_total', { 'result' => 'permerror' } );
93 0         0 return;
94             }
95              
96 50         133 eval {
97             my $spf_request = Mail::SPF::Request->new(
98             'versions' => [2],
99             'scope' => $scope,
100             'identity' => $identity,
101             'ip_address' => $self->ip_address(),
102 50         351 'helo_identity' => $self->{'helo_name'},
103             );
104              
105 50         44235 my $spf_result = $spf_server->process($spf_request);
106              
107 50         185340 my $result_code = $spf_result->code();
108 50         485 $self->metric_count( 'senderid_total', {'result' => $result_code } );
109 50         398 $self->dbgout( 'SenderIdCode', $result_code, LOG_INFO );
110              
111 50 50 33     633 if ( ! ( $config->{'hide_none'} && $result_code eq 'none' ) ) {
112 0         0 my $auth_header = Mail::AuthenticationResults::Header::Entry->new()->set_key( 'senderid' )->safe_set_value( $result_code );
113 0         0 $self->add_auth_header( $auth_header );
114             #my $result_local = $spf_result->local_explanation;
115             #my $result_auth = $spf_result->can( 'authority_explanation' ) ? $spf_result->authority_explanation() : '';
116 0         0 my $result_header = $spf_result->received_spf_header();
117 0         0 my ( $header, $value ) = split( ': ', $result_header, 2 );
118 0         0 $self->prepend_header( $header, $value );
119 0         0 $self->dbgout( 'SPFHeader', $result_header, LOG_DEBUG );
120             }
121             };
122 50 50       248 if ( my $error = $@ ) {
123 0         0 $self->handle_exception( $error );
124 0         0 $self->log_error( 'SENDERID Error ' . $error );
125 0         0 $self->metric_count( 'senderid_total', { 'result' => 'error' } );
126 0         0 my $header = Mail::AuthenticationResults::Header::Entry->new()->set_key( 'senderid' )->safe_set_value( 'temperror' );
127 0         0 $self->add_auth_header($header);
128 0         0 return;
129             }
130 50         166 return;
131             }
132              
133             sub close_callback {
134 96     96 0 314 my ( $self ) = @_;
135 96         285 delete $self->{'from_header'};
136 96         266 delete $self->{'helo_name'};
137 96         254 return;
138             }
139              
140             1;
141              
142             __END__
143              
144             =pod
145              
146             =encoding UTF-8
147              
148             =head1 NAME
149              
150             Mail::Milter::Authentication::Handler::SenderID
151              
152             =head1 VERSION
153              
154             version 20191206
155              
156             =head1 DESCRIPTION
157              
158             Implements the SenderID standard checks.
159              
160             =head1 CONFIGURATION
161              
162             "SenderID" : { | Config for the SenderID Module
163             "hide_none" : 1 | Hide auth line if the result is 'none'
164             },
165              
166             =head1 AUTHOR
167              
168             Marc Bradshaw <marc@marcbradshaw.net>
169              
170             =head1 COPYRIGHT AND LICENSE
171              
172             This software is copyright (c) 2018 by Marc Bradshaw.
173              
174             This is free software; you can redistribute it and/or modify it under
175             the same terms as the Perl 5 programming language system itself.
176              
177             =cut