File Coverage

blib/lib/Mail/Milter/Authentication/Handler.pm
Criterion Covered Total %
statement 1020 1192 85.5
branch 280 398 70.3
condition 48 69 69.5
subroutine 124 136 91.1
pod 99 99 100.0
total 1571 1894 82.9


line stmt bran cond sub pod time code
1             package Mail::Milter::Authentication::Handler;
2 99     99   551 use strict;
  99         198  
  99         2518  
3 99     99   1038 use warnings;
  99         201  
  99         3786  
4             our $VERSION = '20191206'; # VERSION
5              
6              
7 99     99   797 use Digest::MD5 qw{ md5_hex };
  99         208  
  99         5662  
8 99     99   594 use English qw{ -no_match_vars };
  99         151  
  99         652  
9 99     99   64626 use Clone qw{ clone };
  99         189442  
  99         4363  
10 99     99   36377 use Mail::SPF;
  99         6409005  
  99         3537  
11 99     99   38010 use MIME::Base64;
  99         57814  
  99         5154  
12 99     99   704 use Net::DNS::Resolver;
  99         210  
  99         1833  
13 99     99   56234 use Net::IP;
  99         4656122  
  99         17444  
14 99     99   60582 use Sys::Syslog qw{:standard :macros};
  99         936324  
  99         24174  
15 99     99   869 use Sys::Hostname;
  99         164  
  99         4954  
16 99     99   597 use Time::HiRes qw{ ualarm gettimeofday };
  99         198  
  99         1034  
17              
18 99     99   12978 use Mail::Milter::Authentication::Constants qw { :all };
  99         202  
  99         25675  
19 99     99   688 use Mail::Milter::Authentication::Config;
  99         198  
  99         4160  
20 99     99   44491 use Mail::Milter::Authentication::Exception;
  99         253  
  99         2601  
21 99     99   37941 use Mail::Milter::Authentication::Resolver;
  99         255  
  99         3064  
22 99     99   36283 use Mail::AuthenticationResults 1.20180328;
  99         1677022  
  99         2771  
23 99     99   775 use Mail::AuthenticationResults::Header;
  99         157  
  99         1776  
24 99     99   453 use Mail::AuthenticationResults::Header::AuthServID;
  99         244  
  99         7051  
25              
26             our $TestResolver; # For Testing
27              
28              
29             sub new {
30 763     763 1 2372 my ( $class, $thischild ) = @_;
31 763         2474 my $self = {
32             'thischild' => $thischild,
33             };
34 763         2364 bless $self, $class;
35 763         2103 return $self;
36             }
37              
38              
39             sub get_version {
40 0     0 1 0 my ( $self ) = @_;
41             {
42 99     99   548 no strict 'refs'; ## no critic;
  99         200  
  99         805536  
  0         0  
43 0         0 return ${ ref( $self ) . "::VERSION" }; # no critic;
  0         0  
44             }
45 0         0 return;
46             }
47              
48              
49             sub get_json {
50 5     5 1 24 my ( $self, $file ) = @_;
51 5         14 my $basefile = __FILE__;
52 5         43 $basefile =~ s/Handler\.pm$/Handler\/$file/;
53 5         14 $basefile .= '.json';
54 5 50       148 if ( ! -e $basefile ) {
55 0         0 die 'json file ' . $file . ' not found';
56             }
57 5         242 open my $InF, '<', $basefile;
58 5         438 my @Content = <$InF>;
59 5         63 close $InF;
60 5         168 return join( q{}, @Content );
61             }
62              
63              
64             sub metric_register {
65 0     0 1 0 my ( $self, $id, $help ) = @_;
66 0         0 $self->{'thischild'}->{'metric'}->register( $id, $help, $self->{'thischild'} );
67 0         0 return;
68             }
69              
70              
71             sub metric_count {
72 8868     8868 1 20027 my ( $self, $count_id, $labels, $count ) = @_;
73 8868 100       20950 $labels = {} if ! defined $labels;
74 8868 100       18819 $count = 1 if ! defined $count;
75              
76 8868         18262 my $metric = $self->{'thischild'}->{'metric'};
77             $metric->count({
78             'count_id' => $count_id,
79             'labels' => $labels,
80 8868         54473 'server' => $self->{'thischild'},
81             'count' => $count,
82             });
83 8868         27343 return;
84             }
85              
86              
87             sub metric_send {
88 0     0 1 0 my ( $self ) = @_;
89             # NOOP
90             # TODO Deprecate and remove
91 0         0 return;
92             }
93              
94              
95             sub rbl_check_ip {
96 4     4 1 6157 my ( $self, $ip, $list ) = @_;
97              
98 4         7 my $lookup_ip;
99              
100             # Reverse the IP
101 4 100       11 if ( $ip->version() == 4 ) {
    50          
102 2         20 $lookup_ip = join( '.', reverse( split( /\./, $ip->ip() ) ) );
103             }
104             elsif ( $ip->version() == 6 ) {
105 2         29 my $ip_string = $ip->ip();
106 2         21 $ip_string =~ s/://g;
107 2         16 $lookup_ip = join( '.', reverse( split( '', $ip_string ) ) );
108             }
109              
110 4 50       27 return 0 if ! $lookup_ip;
111 4         9 return $self->rbl_check_domain( $lookup_ip, $list );
112             }
113              
114              
115             sub rbl_check_domain {
116 6     6 1 570 my ( $self, $domain, $list ) = @_;
117 6         24 my $resolver = $self->get_object( 'resolver' );
118 6         18 my $lookup = join( '.', $domain, $list );
119 6         27 my $packet = $resolver->query( $lookup, 'A' );
120              
121 6 100       8425 if ($packet) {
122 3         9 foreach my $rr ( $packet->answer ) {
123 3 50       28 if ( lc $rr->type eq 'a' ) {
124 3         30 return $rr->address();
125             }
126             }
127             }
128 3         15 return 0;
129             }
130              
131              
132             sub get_microseconds {
133 27406     27406 1 52422 my ( $self ) = @_;
134 27406         73648 my ($seconds, $microseconds) = gettimeofday;
135 27406         73028 return ( ( $seconds * 1000000 ) + $microseconds );
136             }
137              
138              
139             sub get_microseconds_since {
140 7771     7771 1 16240 my ( $self, $since ) = @_;
141 7771         15482 my $now = $self->get_microseconds();
142 7771         13937 my $elapsed = $now - $since;
143 7771 50       17406 $elapsed = 1 if $elapsed == 0; # Always return at least 1
144 7771         23198 return $elapsed;
145             }
146              
147             # Top Level Callbacks
148              
149              
150             sub register_metrics {
151             return {
152 32     32 1 305 'connect_total' => 'The number of connections made to authentication milter',
153             'callback_error_total' => 'The number of errors in callbacks',
154             'time_microseconds_total' => 'The time in microseconds spent in various handlers',
155             };
156             }
157              
158              
159             sub top_setup_callback {
160              
161 65     65 1 219 my ( $self ) = @_;
162 65         471 $self->status('setup');
163 65         597 $self->dbgout( 'CALLBACK', 'Setup', LOG_DEBUG );
164 65         503 $self->set_return( $self->smfis_continue() );
165              
166 65         372 my $callbacks = $self->get_callbacks( 'setup' );
167 65         323 foreach my $handler ( @$callbacks ) {
168 111         621 $self->dbgout( 'CALLBACK', 'Setup ' . $handler, LOG_DEBUG );
169 111         481 my $start_time = $self->get_microseconds();
170 111         441 $self->get_handler($handler)->setup_callback();
171 111         899 $self->metric_count( 'time_microseconds_total', { 'callback' => 'setup', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
172             }
173 65         539 $self->status('postsetup');
174 65         207 return;
175             }
176              
177              
178             sub is_exception_type {
179 11070     11070 1 22254 my ( $self, $exception ) = @_;
180 11070 50       29771 return if ! defined $exception;
181 11070 100       29888 return if ! $exception;
182 126 100       498 return if ref $exception ne 'Mail::Milter::Authentication::Exception';
183 49   50     200 my $Type = $exception->{ 'Type' } || 'Unknown';
184 49         207 return $Type;
185             }
186              
187              
188             sub handle_exception {
189 11045     11045 1 31452 my ( $self, $exception ) = @_;
190 11045         29999 my $Type = $self->is_exception_type( $exception );
191 11045 100       27864 return if ! $Type;
192 24 50       581 die $exception if $Type eq 'Timeout';
193             #my $Text = $exception->{ 'Text' } || 'Unknown';
194 0         0 return;
195             }
196              
197              
198             sub get_time_remaining {
199 123     123 1 362 my ( $self ) = @_;
200 123         386 my $top_handler = $self->get_top_handler();
201 123 50       450 return if ! exists $top_handler->{ 'timeout_at' };
202 123         433 my $now = $self->get_microseconds();
203 123         328 my $remaining = $top_handler->{ 'timeout_at' } - $now;
204             # can be zero or -ve
205 123         311 return $remaining;
206             }
207              
208              
209             sub set_alarm {
210 6153     6153 1 11763 my ( $self, $microseconds ) = @_;
211 6153         11461 my $top_handler = $self->get_top_handler();
212 6153         16849 $self->dbgout( 'Timeout set', $microseconds, LOG_DEBUG );
213 6153         50712 ualarm( $microseconds );
214 6153 100       20246 if ( $microseconds == 0 ) {
215 3227         7347 delete $top_handler->{ 'timeout_at' };
216             }
217             else {
218 2926         8256 $top_handler->{ 'timeout_at' } = $self->get_microseconds() + ( $microseconds );
219             }
220 6153         56417 return;
221             }
222              
223              
224             sub set_handler_alarm {
225             # Call this in a handler to set a local alarm, will take the lower value
226             # of the microseconds passed in, or what is left of a higher level timeout.
227 61     61 1 196 my ( $self, $microseconds ) = @_;
228 61         269 my $remaining = $self->get_time_remaining();
229 61 50       273 if ( $remaining < $microseconds ) {
230             # This should already be set of course, but for clarity...
231 0         0 $self->dbgout( 'Handler tmeout set (remaining used)', $remaining, LOG_DEBUG );
232 0         0 ualarm( $remaining );
233             }
234             else {
235 61         262 $self->dbgout( 'Handler tmeout set', $microseconds, LOG_DEBUG );
236 61         735 ualarm( $microseconds );
237             }
238 61         275 return;
239             }
240              
241              
242             sub reset_alarm {
243             # Call this after any local handler timeouts to reset to the overall value remaining
244 61     61 1 209 my ( $self ) = @_;
245 61         407 my $remaining = $self->get_time_remaining();
246 61         245 $self->dbgout( 'Timeout reset', $remaining, LOG_DEBUG );
247 61 50       306 if ( $remaining < 1 ) {
248             # We have already timed out!
249 0         0 die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Reset check timeout' });
250             }
251 61         970 ualarm( $remaining );
252 61         298 return;
253             }
254              
255              
256             sub clear_overall_timeout {
257 30     30 1 105 my ( $self ) = @_;
258 30         133 $self->dbgout( 'Overall timeout', 'Clear', LOG_DEBUG );
259 30         125 my $top_handler = $self->get_top_handler();
260 30         97 delete $top_handler->{ 'overall_timeout' };
261 30         106 return;
262             }
263              
264              
265             sub set_overall_timeout {
266 65     65 1 242 my ( $self, $microseconds ) = @_;
267 65         216 my $top_handler = $self->get_top_handler();
268 65         291 $self->dbgout( 'Overall timeout', $microseconds, LOG_DEBUG );
269 65         184 $top_handler->{ 'overall_timeout' } = $self->get_microseconds() + $microseconds;
270 65         242 return;
271             }
272              
273              
274             sub get_type_timeout {
275 3251     3251 1 8591 my ( $self, $type ) = @_;
276              
277 3251         5495 my @log;
278 3251         8406 push @log, "Type: $type";
279              
280 3251         5313 my $effective;
281              
282             my $timeout;
283 3251         6518 my $config = $self->config();
284 3251 100       11900 if ( $config->{ $type . '_timeout' } ) {
285 2891         6570 $timeout = $config->{ $type . '_timeout' } * 1000000;
286 2891         5044 $effective = $timeout;
287 2891         6456 push @log, "Section: $timeout";
288             }
289              
290 3251         4774 my $remaining;
291 3251         6693 my $top_handler = $self->get_top_handler();
292 3251 100       8004 if ( my $overall_timeout = $top_handler->{ 'overall_timeout' } ) {
293 704         2059 my $now = $self->get_microseconds();
294 704         1443 $remaining = $overall_timeout - $now;
295 704         1924 push @log, "Overall: $remaining";
296 704 50       2644 if ( $remaining < 1 ) {
297 0         0 push @log, "Overall Timedout";
298 0         0 $remaining = 10; # arb low value;
299             }
300             }
301              
302 3251 100       6805 if ( $remaining ) {
303 704 100       2003 if ( $timeout ) {
304 669 100       1790 if ( $remaining < $timeout ) {
305 488         939 $effective = $remaining;
306             }
307             }
308             else {
309 35         87 $effective = $remaining;
310             }
311             }
312              
313 3251 100       9018 push @log, "Effective: $effective" if $effective;
314              
315 3251         14749 $self->dbgout( 'Timeout set', join( ', ', @log ), LOG_DEBUG );
316              
317 3251         13683 return $effective;
318             }
319              
320              
321             sub check_timeout {
322 8091     8091 1 17870 my ( $self ) = @_;
323 8091         17796 my $top_handler = $self->get_top_handler();
324 8091 100       21871 return if ! exists $top_handler->{ 'timeout_at' };
325 8022 50       19675 return if $top_handler->{ 'timeout_at' } >= $self->get_microseconds();
326 0         0 delete $top_handler->{ 'timeout_at' };
327 0         0 ualarm( 0 );
328 0         0 die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Manual check timeout' });
329             }
330              
331             sub _remap_ip_and_helo {
332 138     138   366 my ( $self ) = @_;
333              
334 138         413 my $config = $self->config();
335 138 100       1010 if ( exists ( $config->{ 'ip_map' } ) ) {
336 112         243 my $ip_object = $self->{ 'raw_ip_object' };
337 112         231 my $helo_host = $self->{'raw_helo_name'};
338 112         182 foreach my $ip_map ( sort keys %{ $config->{ 'ip_map' } } ) {
  112         590  
339 208         750 my $map_obj = Net::IP->new( $ip_map );
340 208   100     160967 my $is_overlap = $ip_object->overlaps($map_obj) || 0;
341 208 100 66     31644 if (
      66        
      66        
342             $is_overlap == $IP_A_IN_B_OVERLAP
343             || $is_overlap == $IP_B_IN_A_OVERLAP # Should never happen
344             || $is_overlap == $IP_PARTIAL_OVERLAP # Should never happen
345             || $is_overlap == $IP_IDENTICAL
346             )
347             {
348 16         45 my $mapped_to = $config->{ 'ip_map' }->{ $ip_map };
349 16 100 100     137 if ( $helo_host && exists $mapped_to->{helo_map} && exists $mapped_to->{helo_map}->{ $helo_host } ) {
      100        
350             # We have a specific HELO mapping for this!
351 2         6 $mapped_to = $mapped_to->{helo_map}->{ $helo_host };
352             return {
353             ip => Net::IP->new( $mapped_to->{ip} ),
354             helo => $mapped_to->{helo},
355 2         7 };
356             }
357             else {
358             # Remap based on IP Only
359             return {
360             ip => Net::IP->new( $mapped_to->{ip} ),
361             helo => $mapped_to->{helo},
362 14         65 };
363             }
364             }
365             }
366             }
367 122         405 return;
368             }
369              
370              
371             sub remap_connect_callback {
372 69     69 1 63597 my ( $self, $hostname, $ip ) = @_;
373 69         353 $self->{'raw_ip_object'} = $ip;
374 69         505 my $ip_remap = $self->_remap_ip_and_helo();
375 69 100       4813 if ( $ip_remap ) {
376 8         22 $ip = $ip_remap->{ip};
377 8         38 $self->dbgout( 'RemappedConnect', $self->{'raw_ip_object'}->ip() . ' > ' . $ip->ip(), LOG_DEBUG );
378             }
379 69         315 $self->{'ip_object'} = $ip;
380 69         284 return;
381             }
382              
383              
384             sub top_connect_callback {
385              
386             # On Connect
387 215     215 1 758 my ( $self, $hostname, $ip ) = @_;
388 215         960 $self->metric_count( 'connect_total' );
389 215         922 $self->status('connect');
390 215         1049 $self->dbgout( 'CALLBACK', 'Connect', LOG_DEBUG );
391 215         806 $self->set_return( $self->smfis_continue() );
392 215         915 $self->clear_reject_mail();
393 215         797 $self->clear_defer_mail();
394 215         763 $self->clear_quarantine_mail();
395 215         588 my $config = $self->config();
396 215         440 eval {
397 215     1   5390 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Connect callback timeout' }) };
  1         1000261  
398 215 50       1235 if ( my $timeout = $self->get_type_timeout( 'connect' ) ) {
399 215         752 $self->set_alarm( $timeout );
400             }
401              
402 215         1482 $self->dbgout( 'ConnectFrom', $ip->ip(), LOG_DEBUG );
403              
404 215         781 my $callbacks = $self->get_callbacks( 'connect' );
405 215         756 foreach my $handler ( @$callbacks ) {
406 328         1743 $self->dbgout( 'CALLBACK', 'Connect ' . $handler, LOG_DEBUG );
407 328         842 my $start_time = $self->get_microseconds();
408 328         615 eval{ $self->get_handler($handler)->connect_callback( $hostname, $ip ); };
  328         912  
409 328 100       1070 if ( my $error = $@ ) {
410 1         16 $self->handle_exception( $error );
411 0         0 $self->log_error( 'Connect callback error ' . $error );
412 0         0 $self->exit_on_close();
413 0         0 $self->tempfail_on_error();
414 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'connect', 'handler' => $handler } );
415             }
416 327         1877 $self->metric_count( 'time_microseconds_total', { 'callback' => 'connect', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
417 327         1238 $self->check_timeout();
418             }
419 214         786 $self->set_alarm(0);
420             };
421 215 100       1059 if ( my $error = $@ ) {
422 1 50       4 if ( my $type = $self->is_exception_type( $error ) ) {
423 1         12 $self->log_error( 'Connect callback error ' . $type . ' - ' . $error->{ 'Text' } );
424 1         20 $self->metric_count( 'callback_error_total', { 'stage' => 'connect', 'type' => $type } );
425             }
426             else {
427 0         0 $self->log_error( 'Connect callback error ' . $error );
428 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'connect' } );
429             }
430 1         16 $self->exit_on_close();
431 1         6 $self->tempfail_on_error();
432             }
433 215         954 $self->status('postconnect');
434 215         713 return $self->get_return();
435             }
436              
437              
438             sub remap_helo_callback {
439 69     69 1 275 my ( $self, $helo_host ) = @_;
440 69 50       302 if ( !( $self->{'helo_name'} ) ) {
441              
442 69         223 $self->{'raw_helo_name'} = $helo_host;
443 69         220 my $ip_remap = $self->_remap_ip_and_helo();
444 69 100       4849 if ( $ip_remap ) {
445 8         25 my $ip = $ip_remap->{ip};
446 8 100       33 if ( $self->{'ip_object'}->ip() ne $ip_remap->{ip}->ip() ) {
447             # The mapped IP has been changed based on the HELO host received
448 2         20 $self->{'ip_object'} = $ip;
449 2         6 $self->dbgout( 'RemappedConnectHELO', $self->{'ip_object'}->ip() . ' > ' . $ip->ip(), LOG_DEBUG );
450             }
451 8         72 $helo_host = $ip_remap->{helo};
452 8         47 $self->dbgout( 'RemappedHELO', $self->{'raw_helo_name'} . ' > ' . $helo_host, LOG_DEBUG );
453             }
454              
455 69         391 $self->{'helo_name'} = $helo_host;
456             }
457 69         231 return;
458             }
459              
460              
461             sub top_helo_callback {
462              
463             # On HELO
464 215     215 1 654 my ( $self, $helo_host ) = @_;
465 215         733 $self->status('helo');
466 215         906 $self->dbgout( 'CALLBACK', 'Helo', LOG_DEBUG );
467 215         676 $self->set_return( $self->smfis_continue() );
468 215 50       753 $helo_host = q{} if ! defined $helo_host;
469 215         587 my $config = $self->config();
470 215         496 eval {
471 215     1   3894 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'HELO callback timeout' }) };
  1         1000182  
472 215 50       1036 if ( my $timeout = $self->get_type_timeout( 'command' ) ) {
473 215         565 $self->set_alarm( $timeout );
474             }
475              
476             # Take only the first HELO from a connection
477 215 50       867 if ( !( $self->{'seen_helo_name'} ) ) {
478 215         718 $self->{'seen_helo_name'} = $helo_host;
479              
480 215         715 my $callbacks = $self->get_callbacks( 'helo' );
481 215         784 foreach my $handler ( @$callbacks ) {
482 293         1345 $self->dbgout( 'CALLBACK', 'Helo ' . $handler, LOG_DEBUG );
483 293         781 my $start_time = $self->get_microseconds();
484 293         581 eval{ $self->get_handler($handler)->helo_callback($helo_host); };
  293         808  
485 293 100       859 if ( my $error = $@ ) {
486 1         9 $self->handle_exception( $error );
487 0         0 $self->log_error( 'HELO callback error ' . $error );
488 0         0 $self->exit_on_close();
489 0         0 $self->tempfail_on_error();
490 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'helo', 'handler' => $handler } );
491             }
492 292         1419 $self->metric_count( 'time_microseconds_total', { 'callback' => 'helo', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
493 292         1091 $self->check_timeout();
494             }
495             }
496             else {
497 0         0 $self->dbgout('Multiple HELO callbacks detected and ignored', $self->{'seen_helo_name'} . ' / ' . $helo_host, LOG_DEBUG );
498             }
499              
500 214         615 $self->set_alarm(0);
501             };
502 215 100       1022 if ( my $error = $@ ) {
503 1 50       6 if ( my $type = $self->is_exception_type( $error ) ) {
504 1         11 $self->log_error( 'HELO error ' . $type . ' - ' . $error->{ 'Text' } );
505 1         8 $self->metric_count( 'callback_error_total', { 'stage' => 'helo', 'type' => $type } );
506             }
507             else {
508 0         0 $self->log_error( 'HELO callback error ' . $error );
509 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'helo' } );
510             }
511 1         18 $self->exit_on_close();
512 1         4 $self->tempfail_on_error();
513             }
514 215         917 $self->status('posthelo');
515 215         689 return $self->get_return();
516             }
517              
518              
519             sub top_envfrom_callback {
520              
521             # On MAILFROM
522             #...
523 215     215 1 636 my ( $self, $env_from ) = @_;
524 215         736 $self->status('envfrom');
525 215         864 $self->dbgout( 'CALLBACK', 'EnvFrom', LOG_DEBUG );
526 215         675 $self->set_return( $self->smfis_continue() );
527 215 100       704 $env_from = q{} if ! defined $env_from;
528 215         550 my $config = $self->config();
529 215         445 eval {
530 215     1   3619 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'EnvFrom callback timeout' }) };
  1         1000140  
531 215 50       993 if ( my $timeout = $self->get_type_timeout( 'command' ) ) {
532 215         572 $self->set_alarm( $timeout );
533             }
534              
535             # Reset private data for this MAIL transaction
536 215         615 delete $self->{'auth_headers'};
537 215         426 delete $self->{'pre_headers'};
538 215         483 delete $self->{'add_headers'};
539              
540 215         1701 my $callbacks = $self->get_callbacks( 'envfrom' );
541 215         802 foreach my $handler ( @$callbacks ) {
542 648         2839 $self->dbgout( 'CALLBACK', 'EnvFrom ' . $handler, LOG_DEBUG );
543 648         1701 my $start_time = $self->get_microseconds();
544 648         1258 eval { $self->get_handler($handler)->envfrom_callback($env_from); };
  648         1763  
545 648 100       1752 if ( my $error = $@ ) {
546 1         7 $self->handle_exception( $error );
547 0         0 $self->log_error( 'Env From callback error ' . $error );
548 0         0 $self->exit_on_close();
549 0         0 $self->tempfail_on_error();
550 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'envfrom', 'handler' => $handler } );
551             }
552 647         3117 $self->metric_count( 'time_microseconds_total', { 'callback' => 'envfrom', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
553 647         2399 $self->check_timeout();
554             }
555 214         704 $self->set_alarm(0);
556             };
557 215 100       984 if ( my $error = $@ ) {
558 1 50       4 if ( my $type = $self->is_exception_type( $error ) ) {
559 1         10 $self->log_error( 'EnvFrom error ' . $type . ' - ' . $error->{ 'Text' } );
560 1         17 $self->metric_count( 'callback_error_total', { 'stage' => 'envfrom', 'type' => $type } );
561             }
562             else {
563 0         0 $self->log_error( 'EnvFrom callback error ' . $error );
564 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'envfrom' } );
565             }
566 1         9 $self->exit_on_close();
567 1         4 $self->tempfail_on_error();
568             }
569 215         888 $self->status('postenvfrom');
570 215         719 return $self->get_return();
571             }
572              
573              
574             sub top_envrcpt_callback {
575              
576             # On RCPTTO
577             #...
578 215     215 1 607 my ( $self, $env_to ) = @_;
579 215         767 $self->status('envrcpt');
580 215         834 $self->dbgout( 'CALLBACK', 'EnvRcpt', LOG_DEBUG );
581 215         695 $self->set_return( $self->smfis_continue() );
582 215 50       642 $env_to = q{} if ! defined $env_to;
583 215         642 my $config = $self->config();
584 215         440 eval {
585 215     1   3692 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'EnvRcpt callback timeout' }) };
  1         1000324  
586 215 50       1002 if ( my $timeout = $self->get_type_timeout( 'command' ) ) {
587 215         592 $self->set_alarm( $timeout );
588             }
589              
590 215         772 my $callbacks = $self->get_callbacks( 'envrcpt' );
591 215         1499 foreach my $handler ( @$callbacks ) {
592 83         430 $self->dbgout( 'CALLBACK', 'EnvRcpt ' . $handler, LOG_DEBUG );
593 83         303 my $start_time = $self->get_microseconds();
594 83         216 eval{ $self->get_handler($handler)->envrcpt_callback($env_to); };
  83         297  
595 83 100       345 if ( my $error = $@ ) {
596 1         7 $self->handle_exception( $error );
597 0         0 $self->log_error( 'Rcpt To callback error ' . $error );
598 0         0 $self->exit_on_close();
599 0         0 $self->tempfail_on_error();
600 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'rcptto', 'handler' => $handler } );
601             }
602 82         526 $self->metric_count( 'time_microseconds_total', { 'callback' => 'rcptto', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
603 82         361 $self->check_timeout();
604             }
605 214         602 $self->set_alarm(0);
606             };
607 215 100       959 if ( my $error = $@ ) {
608 1 50       5 if ( my $type = $self->is_exception_type( $error ) ) {
609 1         10 $self->log_error( 'EnvRcpt error ' . $type . ' - ' . $error->{ 'Text' } );
610 1         9 $self->metric_count( 'callback_error_total', { 'stage' => 'rcptto', 'type' => $type } );
611             }
612             else {
613 0         0 $self->log_error( 'EnvRcpt callback error ' . $error );
614 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'rcptto' } );
615             }
616 1         8 $self->exit_on_close();
617 1         5 $self->tempfail_on_error();
618             }
619 215         826 $self->status('postenvrcpt');
620 215         637 return $self->get_return();
621             }
622              
623              
624             sub top_header_callback {
625              
626             # On Each Header
627 1251     1251 1 3649 my ( $self, $header, $value, $original ) = @_;
628 1251         3683 $self->status('header');
629 1251         4088 $self->dbgout( 'CALLBACK', 'Header', LOG_DEBUG );
630 1251         3499 $self->set_return( $self->smfis_continue() );
631 1251 50       3125 $value = q{} if ! defined $value;
632 1251         2821 my $config = $self->config();
633 1251         2377 eval {
634 1251     15   21215 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Header callback timeout' }) };
  15         15002570  
635 1251 50       5216 if ( my $timeout = $self->get_type_timeout( 'content' ) ) {
636 1251         2854 $self->set_alarm( $timeout );
637             }
638 1251 50       3663 if ( my $error = $@ ) {
639 0         0 $self->dbgout( 'inline error $error', '', LOG_DEBUG );
640             }
641              
642 1251         3364 my $callbacks = $self->get_callbacks( 'header' );
643 1251         3069 foreach my $handler ( @$callbacks ) {
644 4245         17766 $self->dbgout( 'CALLBACK', 'Header ' . $handler, LOG_DEBUG );
645 4245         11047 my $start_time = $self->get_microseconds();
646 4245         7003 eval{ $self->get_handler($handler)->header_callback( $header, $value, $original ); };
  4245         10119  
647 4245 100       10368 if ( my $error = $@ ) {
648 15         129 $self->handle_exception( $error );
649 0         0 $self->log_error( 'Header callback error ' . $error );
650 0         0 $self->exit_on_close();
651 0         0 $self->tempfail_on_error();
652 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'header', 'handler' => $handler } );
653             }
654 4230         17659 $self->metric_count( 'time_microseconds_total', { 'callback' => 'header', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
655 4230         15197 $self->check_timeout();
656             }
657 1236         2937 $self->set_alarm(0);
658             };
659 1251 100       4802 if ( my $error = $@ ) {
660 15 50       65 if ( my $type = $self->is_exception_type( $error ) ) {
661 15         524 $self->log_error( 'Header error ' . $type . ' - ' . $error->{ 'text' } );
662 15         111 $self->metric_count( 'callback_error_total', { 'stage' => 'header', 'type' => $type } );
663             }
664             else {
665 0         0 $self->log_error( 'Header callback error ' . $error );
666 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'header' } );
667             }
668 15         137 $self->exit_on_close();
669 15         54 $self->tempfail_on_error();
670             }
671 1251         4425 $self->status('postheader');
672 1251         3809 return $self->get_return();
673             }
674              
675              
676             sub top_eoh_callback {
677              
678             # On End of headers
679 215     215 1 545 my ($self) = @_;
680 215         776 $self->status('eoh');
681 215         833 $self->dbgout( 'CALLBACK', 'EOH', LOG_DEBUG );
682 215         723 $self->set_return( $self->smfis_continue() );
683 215         592 my $config = $self->config();
684 215         437 eval {
685 215     1   4004 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'EOH callback timeout' }) };
  1         1000165  
686 215 50       997 if ( my $timeout = $self->get_type_timeout( 'content' ) ) {
687 215         642 $self->set_alarm( $timeout );
688             }
689              
690 215         739 my $callbacks = $self->get_callbacks( 'eoh' );
691 215         772 foreach my $handler ( @$callbacks ) {
692 310         1492 $self->dbgout( 'CALLBACK', 'EOH ' . $handler, LOG_DEBUG );
693 310         954 my $start_time = $self->get_microseconds();
694 310         612 eval{ $self->get_handler($handler)->eoh_callback(); };
  310         898  
695 310 100       1059 if ( my $error = $@ ) {
696 1         10 $self->handle_exception( $error );
697 0         0 $self->log_error( 'EOH callback error ' . $error );
698 0         0 $self->exit_on_close();
699 0         0 $self->tempfail_on_error();
700 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'eoh', 'handler' => $handler } );
701             }
702 309         1809 $self->metric_count( 'time_microseconds_total', { 'callback' => 'eoh', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
703 309         1207 $self->check_timeout();
704             }
705 214         618 $self->set_alarm(0);
706             };
707 215 100       979 if ( my $error = $@ ) {
708 1 50       7 if ( my $type = $self->is_exception_type( $error ) ) {
709 1         33 $self->log_error( 'EOH error ' . $type . ' - ' . $error->{ 'text' } );
710 1         9 $self->metric_count( 'callback_error_total', { 'stage' => 'eoh', 'type' => $type } );
711             }
712             else {
713 0         0 $self->log_error( 'EOH callback error ' . $error );
714 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'eoh' } );
715             }
716 1         8 $self->exit_on_close();
717 1         4 $self->tempfail_on_error();
718             }
719 215         904 $self->dbgoutwrite();
720 215         966 $self->status('posteoh');
721 215         910 return $self->get_return();
722             }
723              
724              
725             sub top_body_callback {
726              
727             # On each body chunk
728 215     215 1 725 my ( $self, $body_chunk ) = @_;
729 215         761 $self->status('body');
730 215         983 $self->dbgout( 'CALLBACK', 'Body', LOG_DEBUG );
731 215         850 $self->set_return( $self->smfis_continue() );
732 215         894 my $config = $self->config();
733 215         511 eval {
734 215     1   4359 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Body callback timeout' }) };
  1         1000159  
735 215 50       1129 if ( my $timeout = $self->get_type_timeout( 'content' ) ) {
736 215         696 $self->set_alarm( $timeout );
737             }
738              
739 215         924 my $callbacks = $self->get_callbacks( 'body' );
740 215         773 foreach my $handler ( @$callbacks ) {
741 215         979 $self->dbgout( 'CALLBACK', 'Body ' . $handler, LOG_DEBUG );
742 215         633 my $start_time = $self->get_microseconds();
743 215         471 eval{ $self->get_handler($handler)->body_callback( $body_chunk ); };
  215         746  
744 215 100       736 if ( my $error = $@ ) {
745 1         9 $self->handle_exception( $error );
746 0         0 $self->log_error( 'Body callback error ' . $error );
747 0         0 $self->exit_on_close();
748 0         0 $self->tempfail_on_error();
749 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'body', 'handler' => $handler } );
750             }
751 214         1539 $self->metric_count( 'time_microseconds_total', { 'callback' => 'body', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
752 214         798 $self->check_timeout();
753             }
754 214         695 $self->set_alarm(0);
755             };
756 215 100       1022 if ( my $error = $@ ) {
757 1 50       4 if ( my $type = $self->is_exception_type( $error ) ) {
758 1         33 $self->log_error( 'Body error ' . $type . ' - ' . $error->{ 'text' } );
759 1         8 $self->metric_count( 'callback_error_total', { 'stage' => 'body', 'type' => $type } );
760             }
761             else {
762 0         0 $self->log_error( 'Body callback error ' . $error );
763 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'body' } );
764             }
765 1         9 $self->exit_on_close();
766 1         5 $self->tempfail_on_error();
767             }
768 215         860 $self->dbgoutwrite();
769 215         723 $self->status('postbody');
770 215         758 return $self->get_return();
771             }
772              
773              
774             sub top_eom_callback {
775              
776             # On End of Message
777 215     215 1 570 my ($self) = @_;
778 215         698 $self->status('eom');
779 215         871 $self->dbgout( 'CALLBACK', 'EOM', LOG_DEBUG );
780 215         715 $self->set_return( $self->smfis_continue() );
781 215         600 my $config = $self->config();
782 215         459 eval {
783 215     1   4144 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'EOM callback timeout' }) };
  1         1000181  
784 215 50       1030 if ( my $timeout = $self->get_type_timeout( 'content' ) ) {
785 215         611 $self->set_alarm( $timeout );
786             }
787              
788 215         754 my $callbacks = $self->get_callbacks( 'eom' );
789 215         742 foreach my $handler ( @$callbacks ) {
790 401         2014 $self->dbgout( 'CALLBACK', 'EOM ' . $handler, LOG_DEBUG );
791 401         1043 my $start_time = $self->get_microseconds();
792 401         912 eval{ $self->get_handler($handler)->eom_callback(); };
  401         1109  
793 401 100       1306 if ( my $error = $@ ) {
794 1         9 $self->handle_exception( $error );
795 0         0 $self->log_error( 'EOM callback error ' . $error );
796 0         0 $self->exit_on_close();
797 0         0 $self->tempfail_on_error();
798 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'eom', 'handler' => $handler } );
799             }
800 400         2145 $self->metric_count( 'time_microseconds_total', { 'callback' => 'eom', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
801 400         1741 $self->check_timeout();
802             }
803 214         664 $self->set_alarm(0);
804             };
805 215 100       1020 if ( my $error = $@ ) {
806 1 50       6 if ( my $type = $self->is_exception_type( $error ) ) {
807 1         31 $self->log_error( 'EOM error ' . $type . ' - ' . $error->{ 'text' } );
808 1         8 $self->metric_count( 'callback_error_total', { 'stage' => 'eom', 'type' => $type } );
809             }
810             else {
811 0         0 $self->log_error( 'EOM callback error ' . $error );
812 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'eom' } );
813             }
814 1         8 $self->exit_on_close();
815 1         5 $self->tempfail_on_error();
816             }
817 215         1056 $self->apply_policy();
818 215         1137 $self->add_headers();
819 215         823 $self->dbgoutwrite();
820 215         936 $self->status('posteom');
821 215         811 return $self->get_return();
822             }
823              
824              
825             sub apply_policy {
826 215     215 1 572 my ($self) = @_;
827              
828 215         412 my @auth_headers;
829 215         552 my $top_handler = $self->get_top_handler();
830 215 100       749 if ( exists( $top_handler->{'c_auth_headers'} ) ) {
831 82         185 @auth_headers = @{ $top_handler->{'c_auth_headers'} };
  82         331  
832             }
833 215 100       715 if ( exists( $top_handler->{'auth_headers'} ) ) {
834 186         396 @auth_headers = ( @auth_headers, @{ $top_handler->{'auth_headers'} } );
  186         627  
835             }
836              
837             #my $parsed_headers = Mail::AuthenticationResults::Parser->new( \@auth_headers );;
838              
839             #use Data::Dumper;
840             #print Dumper \@structured_headers;
841              
842 215         459 return;
843             }
844              
845              
846             sub top_abort_callback {
847              
848             # On any out of our control abort
849 26     26 1 83 my ($self) = @_;
850 26         110 $self->status('abort');
851 26         125 $self->dbgout( 'CALLBACK', 'Abort', LOG_DEBUG );
852 26         108 $self->set_return( $self->smfis_continue() );
853 26         68 my $config = $self->config();
854 26         58 eval {
855 26     1   546 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Abord callback timeout' }) };
  1         1000115  
856 26 50       131 if ( my $timeout = $self->get_type_timeout( 'command' ) ) {
857 26         96 $self->set_alarm( $timeout );
858             }
859              
860 26         104 my $callbacks = $self->get_callbacks( 'abort' );
861 26         159 foreach my $handler ( @$callbacks ) {
862 1         7 $self->dbgout( 'CALLBACK', 'Abort ' . $handler, LOG_DEBUG );
863 1         3 my $start_time = $self->get_microseconds();
864 1         3 eval{ $self->get_handler($handler)->abort_callback(); };
  1         5  
865 1 50       8 if ( my $error = $@ ) {
866 1         7 $self->handle_exception( $error );
867 0         0 $self->log_error( 'Abort callback error ' . $error );
868 0         0 $self->exit_on_close();
869 0         0 $self->tempfail_on_error();
870 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'abort', 'handler' => $handler } );
871             }
872 0         0 $self->metric_count( 'time_microseconds_total', { 'callback' => 'abort', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
873 0         0 $self->check_timeout();
874             }
875 25         93 $self->set_alarm(0);
876             };
877 26 100       154 if ( my $error = $@ ) {
878 1 50       4 if ( my $type = $self->is_exception_type( $error ) ) {
879 1         23 $self->log_error( 'Abort error ' . $type . ' - ' . $error->{ 'text' } );
880 1         6 $self->metric_count( 'callback_error_total', { 'stage' => 'abort', 'type' => $type } );
881             }
882             else {
883 0         0 $self->log_error( 'Abort callback error ' . $error );
884 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'abort' } );
885             }
886 1         8 $self->exit_on_close();
887 1         6 $self->tempfail_on_error();
888             }
889 26         122 $self->status('postabort');
890 26         89 return $self->get_return();
891             }
892              
893              
894             sub top_close_callback {
895              
896             # On end of connection
897 109     109 1 324 my ($self) = @_;
898 109         434 $self->status('close');
899 109         445 $self->dbgout( 'CALLBACK', 'Close', LOG_DEBUG );
900 109         533 $self->set_return( $self->smfis_continue() );
901 109         404 $self->clear_reject_mail();
902 109         418 $self->clear_defer_mail();
903 109         430 $self->clear_quarantine_mail();
904 109         324 my $config = $self->config();
905 109         234 eval {
906 109     2   2029 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'Close callback timeout' }) };
  2         1272671  
907 109 50       744 if ( my $timeout = $self->get_type_timeout( 'command' ) ) {
908 109         365 $self->set_alarm( $timeout );
909             }
910              
911 109         518 my $callbacks = $self->get_callbacks( 'close' );
912 109         426 foreach my $handler ( @$callbacks ) {
913 1056         4838 $self->dbgout( 'CALLBACK', 'Close ' . $handler, LOG_DEBUG );
914 1056         2918 my $start_time = $self->get_microseconds();
915 1056         1980 eval{ $self->get_handler($handler)->close_callback(); };
  1056         2634  
916 1056 100       2851 if ( my $error = $@ ) {
917 1         10 $self->handle_exception( $error );
918 0         0 $self->log_error( 'Close callback error ' . $error );
919 0         0 $self->exit_on_close();
920 0         0 $self->tempfail_on_error();
921 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'close', 'handler' => $handler } );
922             }
923 1055         4593 $self->metric_count( 'time_microseconds_total', { 'callback' => 'close', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
924 1055         3930 $self->check_timeout();
925             }
926 108         449 $self->set_alarm(0);
927             };
928 109 100       633 if ( my $error = $@ ) {
929 1 50       3 if ( my $type = $self->is_exception_type( $error ) ) {
930 1         31 $self->log_error( 'Close error ' . $type . ' - ' . $error->{ 'text' } );
931 1         7 $self->metric_count( 'callback_error_total', { 'stage' => 'close', 'type' => $type } );
932             }
933             else {
934 0         0 $self->log_error( 'Close callback error ' . $error );
935 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'close' } );
936             }
937 1         8 $self->exit_on_close();
938 1         4 $self->tempfail_on_error();
939             }
940 109         328 delete $self->{'helo_name'};
941 109         331 delete $self->{'seen_helo_name'};
942 109         296 delete $self->{'raw_helo_name'};
943 109         1650 delete $self->{'c_auth_headers'};
944 109         1965 delete $self->{'auth_headers'};
945 109         438 delete $self->{'pre_headers'};
946 109         261 delete $self->{'add_headers'};
947 109         278 delete $self->{'ip_object'};
948 109         591 delete $self->{'raw_ip_object'};
949 109         557 $self->dbgoutwrite();
950 109         602 $self->clear_all_symbols();
951 109         512 $self->status('postclose');
952 109         499 return $self->get_return();
953             }
954              
955              
956             sub top_addheader_callback {
957 360     360 1 786 my ( $self ) = @_;
958 360         976 my $config = $self->config();
959              
960 360         700 eval {
961 360     0   6900 local $SIG{'ALRM'} = sub{ die Mail::Milter::Authentication::Exception->new({ 'Type' => 'Timeout', 'Text' => 'AddHeader callback timeout' }) };
  0         0  
962 360 100       1626 if ( my $timeout = $self->get_type_timeout( 'addheader' ) ) {
963 35         143 $self->set_alarm( $timeout );
964             }
965              
966 360         1180 my $callbacks = $self->get_callbacks( 'addheader' );
967 360         908 foreach my $handler ( @$callbacks ) {
968 104         330 my $start_time = $self->get_microseconds();
969 104         406 $self->get_handler($handler)->addheader_callback($self);
970 104         772 $self->metric_count( 'time_microseconds_total', { 'callback' => 'addheader', 'handler' => $handler }, $self->get_microseconds_since( $start_time ) );
971 104         451 $self->check_timeout();
972             }
973 360         970 $self->set_alarm(0);
974             };
975 360 50       1419 if ( my $error = $@ ) {
976 0 0       0 if ( my $type = $self->is_exception_type( $error ) ) {
977 0         0 $self->log_error( 'AddHeader error ' . $type . ' - ' . $error->{ 'text' } );
978 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'addheader', 'type' => $type } );
979             }
980             else {
981 0         0 $self->log_error( 'AddHeader callback error ' . $error );
982 0         0 $self->metric_count( 'callback_error_total', { 'stage' => 'addheader' } );
983             }
984 0         0 $self->exit_on_close();
985 0         0 $self->tempfail_on_error();
986             }
987              
988 360         950 return;
989             }
990              
991              
992             # Other methods
993              
994              
995             sub status {
996 5912     5912 1 12728 my ($self, $status) = @_;
997 5912         12344 my $count = $self->{'thischild'}->{'count'};
998 5912 100       13640 if ( exists ( $self->{'thischild'}->{'smtp'} ) ) {
999 1338 50       3993 if ( $self->{'thischild'}->{'smtp'}->{'count'} ) {
1000 1338         3735 $count .= '.' . $self->{'thischild'}->{'smtp'}->{'count'};
1001             }
1002             }
1003 5912 50       11800 if ( $status ) {
1004 5912         47165 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':processing:' . $status . '(' . $count . ')';
1005             }
1006             else {
1007 0         0 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':processing(' . $count . ')';
1008             }
1009 5912         16432 return;
1010             }
1011              
1012              
1013             sub config {
1014 52939     52939 1 86725 my ($self) = @_;
1015 52939         103767 return $self->{'thischild'}->{'config'};
1016             }
1017              
1018              
1019             sub handler_config {
1020 1625     1625 1 3645 my ($self) = @_;
1021 1625         5147 my $type = $self->handler_type();
1022 1625 50       4387 return if ! $type;
1023 1625 50       4569 if ( $self->is_handler_loaded( $type ) ) {
1024 1625         3490 my $config = $self->config();
1025 1625         3546 my $handler_config = $config->{'handlers'}->{$type};
1026              
1027 1625 50       4480 if ( exists( $config->{ '_external_callback_processor' } ) ) {
1028 0 0       0 if ( $config->{ '_external_callback_processor' }->can( 'handler_config' ) ) {
1029 0         0 $handler_config = clone $handler_config;
1030 0         0 $config->{ '_external_callback_processor' }->handler_config( $type, $handler_config );
1031             }
1032             }
1033              
1034 1625         4677 return $handler_config;
1035             }
1036 0         0 return;
1037             }
1038              
1039              
1040             sub handler_type {
1041 1625     1625 1 3286 my ($self) = @_;
1042 1625         3708 my $type = ref $self;
1043 1625 50       11125 if ( $type eq 'Mail::Milter::Authentication::Handler' ) {
    50          
1044 0         0 return 'Handler';
1045             }
1046             elsif ( $type =~ /^Mail::Milter::Authentication::Handler::(.*)/ ) {
1047 1625         5512 my $handler_type = $1;
1048 1625         4792 return $handler_type;
1049             }
1050             else {
1051 0         0 return undef; ## no critic
1052             }
1053             }
1054              
1055              
1056             sub set_return {
1057 2980     2980 1 6097 my ( $self, $return ) = @_;
1058 2980         5455 my $top_handler = $self->get_top_handler();
1059 2980         5434 $top_handler->{'return_code'} = $return;
1060 2980         4644 return;
1061             }
1062              
1063              
1064             sub get_return {
1065 2891     2891 1 6317 my ( $self ) = @_;
1066 2891         6167 my $top_handler = $self->get_top_handler();
1067 2891 100       6863 if ( defined $self->get_reject_mail() ) {
    50          
    100          
1068 2         12 return $self->smfis_reject();
1069             }
1070             elsif ( defined $self->get_defer_mail() ) {
1071 0         0 return $self->smfis_tempfail();
1072             }
1073             elsif ( defined $self->get_quarantine_mail() ) {
1074             ## TODO Implement this.
1075             }
1076 2889         13899 return $top_handler->{'return_code'};
1077             }
1078              
1079              
1080             sub get_reject_mail {
1081 3573     3573 1 6443 my ( $self ) = @_;
1082 3573         6407 my $top_handler = $self->get_top_handler();
1083 3573         12960 return $top_handler->{'reject_mail'};
1084             }
1085              
1086              
1087             sub clear_reject_mail {
1088 326     326 1 887 my ( $self ) = @_;
1089 326         1400 my $top_handler = $self->get_top_handler();
1090 326         810 delete $top_handler->{'reject_mail'};
1091 326         730 return;
1092             }
1093              
1094              
1095             sub get_defer_mail {
1096 3569     3569 1 6637 my ( $self ) = @_;
1097 3569         6312 my $top_handler = $self->get_top_handler();
1098 3569         11031 return $top_handler->{'defer_mail'};
1099             }
1100              
1101              
1102             sub clear_defer_mail {
1103 324     324 1 800 my ( $self ) = @_;
1104 324         856 my $top_handler = $self->get_top_handler();
1105 324         1873 delete $top_handler->{'defer_mail'};
1106 324         637 return;
1107             }
1108              
1109              
1110              
1111             sub get_quarantine_mail {
1112 3818     3818 1 6958 my ( $self ) = @_;
1113 3818         6422 my $top_handler = $self->get_top_handler();
1114 3818         10135 return $top_handler->{'quarantine_mail'};
1115             }
1116              
1117              
1118             sub clear_quarantine_mail {
1119 324     324 1 853 my ( $self ) = @_;
1120 324         753 my $top_handler = $self->get_top_handler();
1121 324         698 delete $top_handler->{'quarantine_mail'};
1122 324         605 return;
1123             }
1124              
1125              
1126             sub get_top_handler {
1127 136802     136802 1 218931 my ($self) = @_;
1128 136802         212162 my $thischild = $self->{'thischild'};
1129 136802         221209 my $object = $thischild->{'handler'}->{'_Handler'};
1130 136802         231501 return $object;
1131             }
1132              
1133              
1134             sub is_handler_loaded {
1135 7974     7974 1 15980 my ( $self, $name ) = @_;
1136 7974         16772 my $config = $self->config();
1137 7974 100       21690 if ( exists ( $config->{'handlers'}->{$name} ) ) {
1138 7356         18605 return 1;
1139             }
1140 618         2376 return 0;
1141             }
1142              
1143              
1144             sub get_handler {
1145 13586     13586 1 27265 my ( $self, $name ) = @_;
1146 13586         22529 my $thischild = $self->{'thischild'};
1147 13586         27072 my $object = $thischild->{'handler'}->{$name};
1148 13586         63637 return $object;
1149             }
1150              
1151              
1152             sub get_callbacks {
1153 3316     3316 1 7841 my ( $self, $callback ) = @_;
1154 3316         6011 my $thischild = $self->{'thischild'};
1155 3316         9133 return $thischild->{'callbacks_list'}->{$callback};
1156             }
1157              
1158              
1159             sub set_object_maker {
1160 111     111 1 380 my ( $self, $name, $ref ) = @_;
1161 111         415 my $thischild = $self->{'thischild'};
1162 111 100       506 return if $thischild->{'object_maker'}->{$name};
1163 13         84 $thischild->{'object_maker'}->{$name} = $ref;
1164 13         58 return;
1165             }
1166              
1167              
1168             sub get_object {
1169 1218     1218 1 3380 my ( $self, $name ) = @_;
1170              
1171 1218         2597 my $thischild = $self->{'thischild'};
1172 1218         3058 my $object = $thischild->{'object'}->{$name};
1173 1218 100       2930 if ( ! $object ) {
1174              
1175 236 100       1028 if ( exists( $thischild->{'object_maker'}->{$name} ) ) {
    100          
1176 34         127 my $maker = $thischild->{'object_maker'}->{$name};
1177 34         245 &$maker( $self, $name );
1178             }
1179              
1180             elsif ( $name eq 'resolver' ) {
1181 123         480 $self->dbgout( 'Object created', $name, LOG_DEBUG );
1182 123         351 my $config = $self->config();
1183 123   50     692 my $timeout = $config->{'dns_timeout'} || 8;
1184 123   50     574 my $dns_retry = $config->{'dns_retry'} || 2;
1185 123   100     545 my $resolvers = $config->{'dns_resolvers'} || [];
1186 123 50       393 if ( defined $TestResolver ) {
1187 123         264 $object = $TestResolver;
1188 123         2138 warn "Using FAKE TEST DNS Resolver - I Hope this isn't production!";
1189             # If it is you better know what you're doing!
1190             }
1191             else {
1192 0         0 $object = Mail::Milter::Authentication::Resolver->new(
1193             '_handler' => $self,
1194             'udp_timeout' => $timeout,
1195             'tcp_timeout' => $timeout,
1196             'retry' => $dns_retry,
1197             'nameservers' => $resolvers,
1198             );
1199 0         0 $object->udppacketsize(1240);
1200 0         0 $object->persistent_udp(1);
1201             }
1202 123         1121 $thischild->{'object'}->{$name} = {
1203             'object' => $object,
1204             'destroy' => 0,
1205             };
1206             }
1207              
1208             }
1209 1218         4136 return $thischild->{'object'}->{$name}->{'object'};
1210             }
1211              
1212              
1213             sub set_object {
1214 439     439 1 1564 my ( $self, $name, $object, $destroy ) = @_;
1215 439         1395 my $thischild = $self->{'thischild'};
1216 439         1459 $self->dbgout( 'Object set', $name, LOG_DEBUG );
1217 439         2474 $thischild->{'object'}->{$name} = {
1218             'object' => $object,
1219             'destroy' => $destroy,
1220             };
1221 439         1425 return;
1222             }
1223              
1224              
1225             sub destroy_object {
1226 692     692 1 2176 my ( $self, $name ) = @_;
1227 692         1632 my $thischild = $self->{'thischild'};
1228              
1229             # Objects may be set to not be destroyed,
1230             # eg. resolver and spf_server are not
1231             # destroyed for performance reasons
1232             # Resolver, however, has its error cache cleared, as this should only
1233             # cache errors within a single transaction.
1234 692 100       2082 if ($name eq 'resolver' ) {
1235 12 0       609 if ( $thischild->{'object'}->{'resolver'}->can( 'clear_error_cache' ) ) {
1236 0         0 $thischild->{'object'}->{'resolver'}->clear_error_cache();
1237             }
1238             }
1239 680 100       3188 return if ! $thischild->{'object'}->{$name}->{'destroy'};
1240 250 50       865 return if ! $thischild->{'object'}->{$name};
1241 250         1071 $self->dbgout( 'Object destroyed', $name, LOG_DEBUG );
1242 250         10060 delete $thischild->{'object'}->{$name};
1243 250         32910 return;
1244             }
1245              
1246              
1247             sub destroy_all_objects {
1248             # Unused!
1249 18     18 1 83 my ( $self ) = @_;
1250 18         66 my $thischild = $self->{'thischild'};
1251 18         59 foreach my $name ( keys %{ $thischild->{'object'} } )
  18         301  
1252             {
1253 27         107 $self->destroy_object( $name );
1254             }
1255 6         44 return;
1256             }
1257              
1258              
1259             sub exit_on_close {
1260 24     24 1 79 my ( $self ) = @_;
1261 24         73 my $top_handler = $self->get_top_handler();
1262 24         82 $top_handler->{'exit_on_close'} = 1;
1263 24         62 return;
1264             }
1265              
1266              
1267             sub reject_mail {
1268 2     2 1 25 my ( $self, $reason ) = @_;
1269 2         14 my ( $rcode, $xcode, $message ) = split( ' ', $reason, 3 );
1270 2 50 33     99 if ($rcode !~ /^[5]\d\d$/ || $xcode !~ /^[5]\.\d+\.\d+$/ || substr($rcode, 0, 1) ne substr($xcode, 0, 1)) {
      33        
1271 0         0 $self->loginfo ( "Invalid reject message $reason - setting to default" );
1272 0         0 $reason = '550 5.0.0 Message rejected';
1273             }
1274 2         12 my $top_handler = $self->get_top_handler();
1275 2         7 $top_handler->{'reject_mail'} = $reason;
1276 2         7 return;
1277             }
1278              
1279              
1280             sub quarantine_mail {
1281 7     7 1 42 my ( $self, $reason ) = @_;
1282 7         26 my $top_handler = $self->get_top_handler();
1283 7         26 $top_handler->{'quarantine_mail'} = $reason;
1284 7         24 return;
1285             }
1286              
1287              
1288             sub defer_mail {
1289 0     0 1 0 my ( $self, $reason ) = @_;
1290 0         0 my ( $rcode, $xcode, $message ) = split( ' ', $reason, 3 );
1291 0 0 0     0 if ($rcode !~ /^[4]\d\d$/ || $xcode !~ /^[4]\.\d+\.\d+$/ || substr($rcode, 0, 1) ne substr($xcode, 0, 1)) {
      0        
1292 0         0 $self->loginfo ( "Invalid defer message $reason - setting to default" );
1293 0         0 $reason = '450 4.0.0 Message deferred';
1294             }
1295 0         0 my $top_handler = $self->get_top_handler();
1296 0         0 $top_handler->{'defer_mail'} = $reason;
1297 0         0 return;
1298             }
1299              
1300              
1301             sub clear_all_symbols {
1302 109     109 1 313 my ( $self ) = @_;
1303 109         368 my $top_handler = $self->get_top_handler();
1304 109         445 delete $top_handler->{'symbols'};
1305 109         259 return;
1306             }
1307              
1308              
1309             sub clear_symbols {
1310 0     0 1 0 my ( $self ) = @_;
1311 0         0 my $top_handler = $self->get_top_handler();
1312              
1313 0         0 my $connect_symbols;
1314 0 0       0 if ( exists ( $top_handler->{'symbols'} ) ) {
1315 0 0       0 if ( exists ( $top_handler->{'symbols'}->{'C'} ) ) {
1316 0         0 $connect_symbols = $top_handler->{'symbols'}->{'C'};
1317             }
1318             }
1319              
1320 0         0 delete $top_handler->{'symbols'};
1321              
1322 0 0       0 if ( $connect_symbols ) {
1323 0         0 $top_handler->{'symbols'} = {
1324             'C' => $connect_symbols,
1325             };
1326             }
1327              
1328 0         0 return;
1329             }
1330              
1331              
1332             sub set_symbol {
1333 369     369 1 1289 my ( $self, $code, $key, $value ) = @_;
1334 369         1896 $self->dbgout( 'SetSymbol', "$code: $key: $value", LOG_DEBUG );
1335 369         907 my $top_handler = $self->get_top_handler();
1336 369 100       1108 if ( ! exists ( $top_handler->{'symbols'} ) ) {
1337 63         311 $top_handler->{'symbols'} = {};
1338             }
1339 369 100       1276 if ( ! exists ( $top_handler->{'symbols'}->{$code} ) ) {
1340 115         529 $top_handler->{'symbols'}->{$code} = {};
1341             }
1342 369         1335 $top_handler->{'symbols'}->{$code}->{$key} = $value;;
1343 369         1228 return;
1344             }
1345              
1346              
1347             sub get_symbol {
1348 63926     63926 1 115625 my ( $self, $searchkey ) = @_;
1349 63926         110329 my $top_handler = $self->get_top_handler();
1350 63926   100     171043 my $symbols = $top_handler->{'symbols'} || {};
1351 63926         92741 foreach my $code ( keys %{$symbols} ) {
  63926         162445  
1352 60387         91589 my $subsymbols = $symbols->{$code};
1353 60387         81850 foreach my $key ( keys %{$subsymbols} ) {
  60387         122032  
1354 131283 100       261103 if ( $searchkey eq $key ) {
1355 30260         97630 return $subsymbols->{$key};
1356             }
1357             }
1358             }
1359 33666         108643 return;
1360             }
1361              
1362              
1363             sub tempfail_on_error {
1364 24     24 1 67 my ( $self ) = @_;
1365 24         72 my $config = $self->config();
1366 24 50       111 if ( $self->is_authenticated() ) {
    50          
    50          
1367 0 0       0 if ( $config->{'tempfail_on_error_authenticated'} ) {
1368 0         0 $self->log_error('TempFail set');
1369 0         0 $self->set_return( $self->smfis_tempfail() );
1370             }
1371             }
1372             elsif ( $self->is_local_ip_address() ) {
1373 0 0       0 if ( $config->{'tempfail_on_error_local'} ) {
1374 0         0 $self->log_error('TempFail set');
1375 0         0 $self->set_return( $self->smfis_tempfail() );
1376             }
1377             }
1378             elsif ( $self->is_trusted_ip_address() ) {
1379 0 0       0 if ( $config->{'tempfail_on_error_trusted'} ) {
1380 0         0 $self->log_error('TempFail set');
1381 0         0 $self->set_return( $self->smfis_tempfail() );
1382             }
1383             }
1384             else {
1385 24 50       85 if ( $config->{'tempfail_on_error'} ) {
1386 24         110 $self->log_error('TempFail set');
1387 24         102 $self->set_return( $self->smfis_tempfail() );
1388             }
1389             }
1390 24         77 return;
1391             }
1392              
1393              
1394              
1395             # Common calls into other Handlers
1396              
1397              
1398             sub is_local_ip_address {
1399 2032     2032 1 4171 my ($self) = @_;
1400 2032 100       5340 return 0 if ! $self->is_handler_loaded('LocalIP');
1401 1852         4873 return $self->get_handler('LocalIP')->{'is_local_ip_address'};
1402             }
1403              
1404              
1405             sub is_trusted_ip_address {
1406 2385     2385 1 4857 my ($self) = @_;
1407 2385 100       5001 return 0 if ! $self->is_handler_loaded('TrustedIP');
1408 2205         4638 return $self->get_handler('TrustedIP')->{'is_trusted_ip_address'};
1409             }
1410              
1411              
1412             sub is_encrypted {
1413 0     0 1 0 my ($self) = @_;
1414 0 0       0 return undef if ! $self->is_handler_loaded('TLS'); ## no critic
1415 0         0 return $self->get_handler('TLS')->{'is_encrypted'};
1416             }
1417              
1418              
1419             sub is_authenticated {
1420 1648     1648 1 3186 my ($self) = @_;
1421 1648 100       3645 return 0 if ! $self->is_handler_loaded('Auth');
1422 1468         3190 return $self->get_handler('Auth')->{'is_authenticated'};
1423             }
1424              
1425              
1426             sub ip_address {
1427 259     259 1 765 my ($self) = @_;
1428 259         838 my $top_handler = $self->get_top_handler();
1429 259         1939 return $top_handler->{'ip_object'}->ip();
1430             }
1431              
1432              
1433              
1434             # Header formatting and data methods
1435              
1436              
1437             sub format_ctext {
1438              
1439             # Return ctext (but with spaces intact)
1440 364     364 1 661 my ( $self, $text ) = @_;
1441 364 50       749 $text = q{} if ! defined $text;
1442 364         704 $text =~ s/\t/ /g;
1443 364         850 $text =~ s/\n/ /g;
1444 364         545 $text =~ s/\r/ /g;
1445 364         541 $text =~ s/\(/ /g;
1446 364         554 $text =~ s/\)/ /g;
1447 364         514 $text =~ s/\\/ /g;
1448 364         736 return $text;
1449             }
1450              
1451              
1452             sub format_ctext_no_space {
1453 364     364 1 717 my ( $self, $text ) = @_;
1454 364         837 $text = $self->format_ctext($text);
1455 364         680 $text =~ s/ //g;
1456 364         606 $text =~ s/;/_/g;
1457 364         633 return $text;
1458             }
1459              
1460              
1461             sub format_header_comment {
1462 0     0 1 0 my ( $self, $comment ) = @_;
1463 0         0 $comment = $self->format_ctext($comment);
1464 0         0 return $comment;
1465             }
1466              
1467              
1468             sub format_header_entry {
1469 182     182 1 542 my ( $self, $key, $value ) = @_;
1470 182         645 $key = $self->format_ctext_no_space($key);
1471 182         402 $value = $self->format_ctext_no_space($value);
1472 182         488 my $string = "$key=$value";
1473 182         504 return $string;
1474             }
1475              
1476              
1477             sub get_domain_from {
1478 301     301 1 6430 my ( $self, $address ) = @_;
1479 301 50       799 $address = q{} if ! defined $address;
1480 301         939 $address = $self->get_address_from($address);
1481 301         821 my $domain = 'localhost.localdomain';
1482 301         732 $address =~ s/<//g;
1483 301         656 $address =~ s/>//g;
1484 301 100       980 if ( $address =~ /\@/ ) {
1485 298         1353 ($domain) = $address =~ /.*\@(.*)/;
1486             }
1487 301         831 $domain =~ s/\s//g;
1488 301         1812 return lc $domain;
1489             }
1490              
1491              
1492             sub get_domains_from {
1493 147     147 1 950 my ( $self, $addresstxt ) = @_;
1494 147 50       371 $addresstxt = q{} if ! defined $addresstxt;
1495 147         483 my $addresses = $self->get_addresses_from($addresstxt);
1496 147         333 my $domains = [];
1497 147         379 foreach my $address ( @$addresses ) {
1498 157         313 my $domain;
1499 157         343 $address =~ s/<//g;
1500 157         313 $address =~ s/>//g;
1501 157 100       543 if ( $address =~ /\@/ ) {
1502 151         669 ($domain) = $address =~ /.*\@(.*)/;
1503             }
1504 157 100       409 next if ! defined $domain;
1505 151         328 $domain =~ s/\s//g;
1506 151         545 push @$domains, lc $domain;
1507             }
1508 147         540 return $domains;
1509             }
1510              
1511 99     99   1573 use constant IsSep => 0;
  99         202  
  99         8960  
1512 99     99   697 use constant IsPhrase => 1;
  99         180  
  99         4439  
1513 99     99   611 use constant IsEmail => 2;
  99         160  
  99         5143  
1514 99     99   927 use constant IsComment => 3;
  99         284  
  99         257774  
1515              
1516              
1517             sub get_address_from {
1518 551     551 1 7662 my ( $self, $Str ) = @_;
1519 551         1814 my $addresses = $self->get_addresses_from( $Str );
1520 551         2091 return $addresses->[0];
1521             }
1522              
1523              
1524             sub get_addresses_from {
1525 787     787 1 2145 my ( $self, $Str ) = @_;
1526 787 50       1959 $Str = q{} if ! defined $Str;
1527              
1528 787 100       2049 if ( $Str eq q{} ) {
1529 19         124 $self->log_error( 'Could not parse empty address' );
1530 19         70 return [ $Str ];
1531             }
1532              
1533 768         3474 my $IDNComponentRE = qr/[^\x20-\x2c\x2e\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/;
1534 768         5244 my $IDNRE = qr/(?:$IDNComponentRE\.)+$IDNComponentRE/;
1535 768         2314 my $RFC_atom = qr/[a-z0-9\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~]+/i;
1536 768         4316 my $RFC_dotatom = qr/${RFC_atom}(?:\.${RFC_atom})*/;
1537              
1538             # Break everything into Tokens
1539 768         1613 my ( @Tokens, @Types );
1540             TOKEN_LOOP:
1541 768         1535 while (1) {
1542 1822 100       13137 if ($Str =~ m/\G\"(.*?)(?<!\\)(?:\"|\z)\s*/sgc) {
    100          
    100          
    100          
    100          
    50          
1543             # String " ... "
1544 7         28 push @Tokens, $1;
1545 7         19 push @Types, IsPhrase;
1546             }
1547             elsif ( $Str =~ m/\G\<(.*?)(?<!\\)(?:[>,;]|\z)\s*/sgc) {
1548             # String < ... >
1549 119         418 push @Tokens, $1;
1550 119         279 push @Types, IsEmail;
1551             }
1552             elsif ($Str =~ m/\G\((.*?)(?<!\\)\)\s*/sgc) {
1553             # String ( ... )
1554 2         4 push @Tokens, $1;
1555 2         5 push @Types, IsComment;
1556             }
1557             elsif ($Str =~ m/\G[,;]\s*/gc) {
1558             # Comma or semi-colon
1559 16         37 push @Tokens, undef;
1560 16         34 push @Types, IsSep;
1561             }
1562             elsif ($Str =~ m/\G$/gc) {
1563             # End of line
1564 768         1900 last TOKEN_LOOP;
1565             }
1566             elsif ($Str =~ m/\G([^\s,;"<]*)\s*/gc) {
1567             # Anything else
1568 910 50       3339 if (length $1) {
1569 910         2346 push @Tokens, $1;
1570 910         1979 push @Types, IsPhrase;
1571             }
1572             }
1573             else {
1574             # Incomplete line. We'd like to die, but we'll return what we can
1575 0         0 $self->log_error('Could not parse address ' . $Str . ' : Unknown line remainder : ' . substr( $Str, pos() ) );
1576 0         0 push @Tokens, substr($Str, pos($Str));
1577 0         0 push @Types, IsComment;
1578 0         0 last TOKEN_LOOP;
1579             }
1580             }
1581              
1582             # Now massage Tokens into [ "phrase", "emailaddress", "comment" ]
1583 768         1367 my @Addrs;
1584 768         1444 my ($Phrase, $Email, $Comment, $Type);
1585 768         2331 for (my $i = 0; $i < scalar(@Tokens); $i++) {
1586 1054         2685 my ($Type, $Token) = ($Types[$i], $Tokens[$i]);
1587              
1588             # If - a separator OR
1589             # - email address and already got one OR
1590             # - phrase and already got email address
1591             # then add current data as token
1592 1054 100 100     7712 if (($Type == IsSep) ||
      100        
      100        
      100        
1593             ($Type == IsEmail && defined($Email)) ||
1594             ($Type == IsPhrase && defined($Email)) ) {
1595 40 100       147 push @Addrs, $Email if defined $Email;
1596 40         111 ($Phrase, $Email, $Comment) = (undef, undef, undef);
1597             }
1598              
1599             # A phrase...
1600 1054 100       2499 if ($Type == IsPhrase) {
    100          
    100          
1601             # Strip '...' around token
1602 917         1956 $Token =~ s/^'(.*)'$/$1/;
1603             # Strip any newlines assuming folded headers
1604 917         1788 $Token =~ s/\r?\n//g;
1605              
1606             # Email like token?
1607 917 100       5282 if ($Token =~ /^$RFC_dotatom\@$IDNRE$/o) {
1608 674         1614 $Token =~ s/^\s+//;
1609 674         1480 $Token =~ s/\s+$//;
1610 674         1377 $Token =~ s/\s+\@/\@/;
1611 674         1629 $Token =~ s/\@\s+/\@/;
1612             # Yes, check if next token is definitely email. If yes,
1613             # make this a phrase, otherwise make it an email item
1614 674 50 66     2304 if ($i+1 < scalar(@Tokens) && $Types[$i+1] == IsEmail) {
1615 0 0       0 $Phrase = defined($Phrase) ? $Phrase . " " . $Token : $Token;
1616             }
1617             else {
1618             # If we've already got an email address, add current address
1619 674 50       1548 if (defined($Email)) {
1620 0         0 push @Addrs, $Email;
1621 0         0 ($Phrase, $Email, $Comment) = (undef, undef, undef);
1622             }
1623 674         2019 $Email = $Token;
1624             }
1625             }
1626             else {
1627             # No, just add as phrase
1628 243 100       984 $Phrase = defined($Phrase) ? $Phrase . " " . $Token : $Token;
1629             }
1630             }
1631             elsif ($Type == IsEmail) {
1632             # If an email, set email addr. Should be empty
1633 119         384 $Email = $Token;
1634             }
1635             elsif ($Type == IsComment) {
1636 2 50       6 $Comment = defined($Comment) ? $Comment . ", " . $Token : $Token;
1637             }
1638             # Must be separator, do nothing
1639             }
1640              
1641             # Add any remaining addresses
1642 768 100       2205 push @Addrs, $Email if defined($Email);
1643              
1644 768 100       1840 if ( ! @Addrs ) {
1645             # We couldn't parse, so just run with it and hope for the best
1646 10         22 push @Addrs, $Str;
1647 10         43 $self->log_error( 'Could not parse address ' . $Str );
1648             }
1649              
1650 768         1436 my @TidyAddresses;
1651 768         2089 foreach my $Address ( @Addrs ) {
1652              
1653 803 50       2074 next if ( $Address =~ /\@unspecified-domain$/ );
1654              
1655 803 50       2046 if ( $Address =~ /^mailto:(.*)$/ ) {
1656 0         0 $Address = $1;
1657             }
1658              
1659             # Trim whitelist that's possible, but not useful and
1660             # almost certainly a copy/paste issue
1661             # e.g. < foo @ bar.com >
1662              
1663 803         1830 $Address =~ s/^\s+//;
1664 803         1703 $Address =~ s/\s+$//;
1665 803         1678 $Address =~ s/\s+\@/\@/;
1666 803         1743 $Address =~ s/\@\s+/\@/;
1667              
1668 803         2069 push @TidyAddresses, $Address;
1669             }
1670              
1671 768 50       1920 if ( ! @TidyAddresses ) {
1672             # We really couldn't parse, so just run with it and hope for the best
1673 0         0 push @TidyAddresses, $Str;
1674             }
1675              
1676 768         4188 return \@TidyAddresses;
1677              
1678             }
1679              
1680              
1681             sub get_my_hostname {
1682 472     472 1 2285 my ($self) = @_;
1683 472         1171 my $hostname = $self->get_symbol('j');
1684 472 100       1273 if ( ! $hostname ) {
1685 323         640 $hostname = $self->get_symbol('{rcpt_host}');
1686             }
1687 472 100       1123 if ( ! $hostname ) { # Fallback
1688 323         1250 $hostname = hostname;
1689             }
1690 472         3242 return $hostname;
1691             }
1692              
1693              
1694              
1695             # Logging
1696              
1697              
1698             sub dbgout {
1699 24701     24701 1 67075 my ( $self, $key, $value, $priority ) = @_;
1700 24701   100     51378 my $queue_id = $self->get_symbol('i') || q{--};
1701 24701 50       54067 $key = q{--} if ! defined $key;
1702 24701 50       46441 $value = q{--} if ! defined $value;
1703              
1704 24701         50505 my $config = $self->config();
1705 24701 50 66     93265 if (
1706             $priority == LOG_DEBUG
1707             &&
1708             ! $config->{'debug'}
1709             ) {
1710 0         0 return;
1711             }
1712              
1713             # Sys::Syslog and Log::Dispatchouli have different priority models
1714 24701 0       54730 my $log_priority = $priority == LOG_DEBUG ? 'debug'
    0          
    0          
    50          
    50          
    50          
    100          
    100          
1715             : $priority == LOG_INFO ? 'info'
1716             : $priority == LOG_NOTICE ? 'notice'
1717             : $priority == LOG_WARNING ? 'warning'
1718             : $priority == LOG_ERR ? 'error'
1719             : $priority == LOG_CRIT ? 'critical'
1720             : $priority == LOG_ALERT ? 'alert'
1721             : $priority == LOG_EMERG ? 'emergency'
1722             : 'info';
1723              
1724 24701 50       48240 if ( $config->{'logtoerr'} ) {
1725 24701         97818 Mail::Milter::Authentication::_warn( "$queue_id: $key: $value" );
1726             }
1727              
1728 24701         76363 my $top_handler = $self->get_top_handler();
1729 24701 100       61303 if ( !exists( $top_handler->{'dbgout'} ) ) {
1730 10307         29400 $top_handler->{'dbgout'} = [];
1731             }
1732 24701   50     36945 push @{ $top_handler->{'dbgout'} },
  24701   100     142150  
1733             {
1734             'priority' => $log_priority,
1735             'key' => $key || q{},
1736             'value' => $value || q{},
1737             };
1738              
1739             # Write now if we can.
1740 24701 100       57332 if ( $self->get_symbol('i') ) {
1741 9795         24219 $self->dbgoutwrite();
1742             }
1743              
1744 24701         57623 return;
1745             }
1746              
1747              
1748             sub log_error {
1749 122     122 1 381 my ( $self, $error ) = @_;
1750 122         487 $self->dbgout( 'ERROR', $error, LOG_ERR );
1751 122         277 return;
1752             }
1753              
1754              
1755             sub dbgoutwrite {
1756 10549     10549 1 20147 my ($self) = @_;
1757 10549         19563 eval {
1758 10549         22864 my $config = $self->config();
1759 10549   66     21606 my $queue_id = $self->get_symbol('i') ||
1760             'NOQUEUE.' . substr( uc md5_hex( "Authentication Milter Client $PID " . time() . rand(100) ) , -11 );
1761 10549         24528 my $top_handler = $self->get_top_handler();
1762 10549 100       23413 if ( exists( $top_handler->{'dbgout'} ) ) {
1763             LOGENTRY:
1764 10304         17517 foreach my $entry ( @{ $top_handler->{'dbgout'} } ) {
  10304         20326  
1765 24578         7385345 my $key = $entry->{'key'};
1766 24578         46033 my $value = $entry->{'value'};
1767 24578         39505 my $priority = $entry->{'priority'};
1768 24578         57343 my $line = "$queue_id: $key: $value";
1769 24578 50 66     100523 if (
1770             $priority eq 'debug'
1771             &&
1772             ! $config->{'debug'}
1773             ) {
1774 0         0 next LOGENTRY;
1775             }
1776 24578         78558 Mail::Milter::Authentication::logger()->log( { 'level' => $priority }, $line );
1777             }
1778             }
1779 10549         6235029 delete $top_handler->{'dbgout'};
1780             };
1781 10549         38792 $self->handle_exception( $@ ); # Not usually called within an eval, however we shouldn't
1782             # ever get a Timeout (for example) here, so it is safe to
1783             # pass to handle_exception anyway.
1784 10549         19759 return;
1785             }
1786              
1787              
1788              
1789             # Header handling
1790              
1791              
1792             sub can_sort_header {
1793 80     80 1 178 my ( $self, $header ) = @_;
1794 80         243 return 0;
1795             }
1796              
1797              
1798             sub header_sort {
1799 797     797 1 1714 my ( $self, $sa, $sb ) = @_;
1800              
1801 797         1780 my $config = $self->config();
1802              
1803 797         2063 my $string_a;
1804             my $string_b;
1805              
1806 797         0 my $handler_a;
1807 797 50       2080 if ( ref $sa eq 'Mail::AuthenticationResults::Header::Entry' ) {
1808 797         2193 $handler_a = $sa->key();
1809 797         8835 $string_a = $sa->as_string();
1810             }
1811             else {
1812 0         0 ( $handler_a ) = split( '=', $sa, 2 );
1813 0         0 $string_a = $sa;
1814             }
1815 797         523455 my $handler_b;
1816 797 50       2210 if ( ref $sb eq 'Mail::AuthenticationResults::Header::Entry' ) {
1817 797         1987 $handler_b = $sb->key();
1818 797         8327 $string_b = $sb->as_string();
1819             }
1820             else {
1821 0         0 ( $handler_b ) = split( '=', $sb, 2 );
1822 0         0 $string_b = $sb;
1823             }
1824              
1825 797 100       653501 if ( $handler_a eq $handler_b ) {
1826             # Check for a handler specific sort method
1827 40         84 foreach my $name ( @{$config->{'load_handlers'}} ) {
  40         199  
1828 88         206 my $handler = $self->get_handler($name);
1829 88 100       398 if ( $handler->can_sort_header( lc $handler_a ) ) {
1830 8 50       44 if ( $handler->can( 'handler_header_sort' ) ) {
1831 8         30 return $handler->handler_header_sort( $sa, $sb );
1832             }
1833             }
1834             }
1835             }
1836              
1837 789         2387 return $string_a cmp $string_b;
1838             }
1839              
1840             sub _stringify_header {
1841 0     0   0 my ( $self, $header ) = @_;
1842 0 0       0 if ( ref $header eq 'Mail::AuthenticationResults::Header::Entry' ) {
1843 0         0 return $header->as_string();
1844             }
1845 0         0 return $header;
1846             }
1847              
1848              
1849             sub add_headers {
1850 215     215 1 501 my ($self) = @_;
1851              
1852 215         565 my $config = $self->config();
1853              
1854 215         824 my $header = $self->get_my_hostname();
1855 215         526 my $top_handler = $self->get_top_handler();
1856              
1857 215         403 my @auth_headers;
1858 215 100       770 if ( exists( $top_handler->{'c_auth_headers'} ) ) {
1859 82         194 @auth_headers = @{ $top_handler->{'c_auth_headers'} };
  82         244  
1860             }
1861 215 100       667 if ( exists( $top_handler->{'auth_headers'} ) ) {
1862 186         431 @auth_headers = ( @auth_headers, @{ $top_handler->{'auth_headers'} } );
  186         551  
1863             }
1864 215 100       704 if (@auth_headers) {
1865              
1866 207         1025 @auth_headers = sort { $self->header_sort( $a, $b ) } @auth_headers;
  797         2086  
1867              
1868             # Do we have any legacy type headers?
1869 207         504 my $are_string_headers = 0;
1870 207         1925 my $header_obj = Mail::AuthenticationResults::Header->new();
1871 207         2006 foreach my $header ( @auth_headers ) {
1872 620 50       18037 if ( ref $header ne 'Mail::AuthenticationResults::Header::Entry' ) {
1873 0         0 $are_string_headers = 1;
1874 0         0 last;
1875             }
1876 620         2100 $header_obj->add_child( $header );
1877             }
1878              
1879 207 50       8524 if ( $are_string_headers ) {
1880             # We have legacy headers, add in a legacy way
1881 0         0 $header .= ";\n ";
1882 0         0 $header .= join( ";\n ", map { $self->_stringify_header( $_ ) } @auth_headers );
  0         0  
1883             }
1884             else {
1885 207         1350 $header_obj->set_value( Mail::AuthenticationResults::Header::AuthServID->new()->safe_set_value( $self->get_my_hostname() ) );
1886 207         11979 $header_obj->set_eol( "\n" );
1887 207 50       3164 if ( exists( $config->{'header_indent_style'} ) ) {
1888 0         0 $header_obj->set_indent_style( $config->{'header_indent_style'} );
1889             }
1890             else {
1891 207         874 $header_obj->set_indent_style( 'entry' );
1892             }
1893 207 50       8274 if ( exists( $config->{'header_indent_by'} ) ) {
1894 0         0 $header_obj->set_indent_by( $config->{'header_indent_by'} );
1895             }
1896             else {
1897 207         575 $header_obj->set_indent_by( 4 );
1898             }
1899 207 50       1458 if ( exists( $config->{'header_fold_at'} ) ) {
1900 0         0 $header_obj->set_fold_at( $config->{'header_fold_at'} );
1901             }
1902 207         756 $header = $header_obj->as_string();
1903             }
1904              
1905             }
1906             else {
1907 8         23 $header .= '; none';
1908             }
1909              
1910 215         842708 $self->prepend_header( 'Authentication-Results', $header );
1911              
1912 215 100       707 if ( my $reason = $self->get_quarantine_mail() ) {
1913 6         21 $self->prepend_header( 'X-Disposition-Quarantine', $reason );
1914             }
1915              
1916 215         1050 $top_handler->top_addheader_callback();
1917              
1918 215 50       785 if ( exists( $top_handler->{'pre_headers'} ) ) {
1919 215         428 foreach my $header ( @{ $top_handler->{'pre_headers'} } ) {
  215         672  
1920             $self->dbgout( 'PreHeader',
1921 271         1535 $header->{'field'} . ': ' . $header->{'value'}, LOG_INFO );
1922 271         1359 $self->insert_header( 1, $header->{'field'}, $header->{'value'} );
1923             }
1924             }
1925              
1926 215 100       822 if ( exists( $top_handler->{'add_headers'} ) ) {
1927 2         8 foreach my $header ( @{ $top_handler->{'add_headers'} } ) {
  2         10  
1928             $self->dbgout( 'AddHeader',
1929 8         51 $header->{'field'} . ': ' . $header->{'value'}, LOG_INFO );
1930 8         30 $self->add_header( $header->{'field'}, $header->{'value'} );
1931             }
1932             }
1933              
1934 215         579 return;
1935             }
1936              
1937              
1938             sub prepend_header {
1939 271     271 1 994 my ( $self, $field, $value ) = @_;
1940 271         850 my $top_handler = $self->get_top_handler();
1941 271 100       1046 if ( !exists( $top_handler->{'pre_headers'} ) ) {
1942 215         615 $top_handler->{'pre_headers'} = [];
1943             }
1944 271         570 push @{ $top_handler->{'pre_headers'} },
  271         1255  
1945             {
1946             'field' => $field,
1947             'value' => $value,
1948             };
1949 271         644 return;
1950             }
1951              
1952              
1953             sub add_auth_header {
1954 481     481 1 1423 my ( $self, $value ) = @_;
1955 481         1369 my $top_handler = $self->get_top_handler();
1956 481 100       1688 if ( !exists( $top_handler->{'auth_headers'} ) ) {
1957 185         575 $top_handler->{'auth_headers'} = [];
1958             }
1959 481         987 push @{ $top_handler->{'auth_headers'} }, $value;
  481         1218  
1960 481         1298 return;
1961             }
1962              
1963              
1964             sub add_c_auth_header {
1965              
1966             # Connection wide auth headers
1967 139     139 1 396 my ( $self, $value ) = @_;
1968 139         397 my $top_handler = $self->get_top_handler();
1969 139 100       524 if ( !exists( $top_handler->{'c_auth_headers'} ) ) {
1970 81         409 $top_handler->{'c_auth_headers'} = [];
1971             }
1972 139         295 push @{ $top_handler->{'c_auth_headers'} }, $value;
  139         362  
1973 139         343 return;
1974             }
1975              
1976              
1977             sub append_header {
1978 8     8 1 29 my ( $self, $field, $value ) = @_;
1979 8         25 my $top_handler = $self->get_top_handler();
1980 8 100       39 if ( !exists( $top_handler->{'add_headers'} ) ) {
1981 2         8 $top_handler->{'add_headers'} = [];
1982             }
1983 8         18 push @{ $top_handler->{'add_headers'} },
  8         41  
1984             {
1985             'field' => $field,
1986             'value' => $value,
1987             };
1988 8         29 return;
1989             }
1990              
1991              
1992              
1993             # Lower level methods
1994              
1995              
1996             sub smfis_continue {
1997 2956     2956 1 9740 return SMFIS_CONTINUE;
1998             }
1999              
2000              
2001             sub smfis_tempfail {
2002 24     24 1 103 return SMFIS_TEMPFAIL;
2003             }
2004              
2005              
2006             sub smfis_reject {
2007 2     2 1 12 return SMFIS_REJECT;
2008             }
2009              
2010              
2011             sub smfis_discard {
2012 0     0 1 0 return SMFIS_DISCARD;
2013             }
2014              
2015              
2016             sub smfis_accept {
2017 0     0 1 0 return SMFIS_ACCEPT;
2018             }
2019              
2020              
2021              
2022              
2023             sub write_packet {
2024 0     0 1 0 my ( $self, $type, $data ) = @_;
2025 0         0 my $thischild = $self->{'thischild'};
2026 0         0 $thischild->write_packet( $type, $data );
2027 0         0 return;
2028             }
2029              
2030              
2031             sub add_header {
2032 8     8 1 22 my ( $self, $key, $value ) = @_;
2033 8         19 my $thischild = $self->{'thischild'};
2034 8         26 my $config = $self->config();
2035 8 50       28 return if $config->{'dryrun'};
2036 8         57 $thischild->add_header( $key, $value );
2037 8         26 return;
2038             }
2039              
2040              
2041             sub insert_header {
2042 271     271 1 800 my ( $self, $index, $key, $value ) = @_;
2043 271         606 my $thischild = $self->{'thischild'};
2044 271         712 my $config = $self->config();
2045 271 100       933 return if $config->{'dryrun'};
2046 266         1995 $thischild->insert_header( $index, $key, $value );
2047 266         694 return;
2048             }
2049              
2050              
2051             sub change_header {
2052 12     12 1 44 my ( $self, $key, $index, $value ) = @_;
2053 12         28 my $thischild = $self->{'thischild'};
2054 12         38 my $config = $self->config();
2055 12 50       38 return if $config->{'dryrun'};
2056 12         87 $thischild->change_header( $key, $index, $value );
2057 12         48 return;
2058             }
2059              
2060             1;
2061              
2062             __END__
2063              
2064             =pod
2065              
2066             =encoding UTF-8
2067              
2068             =head1 NAME
2069              
2070             Mail::Milter::Authentication::Handler
2071              
2072             =head1 VERSION
2073              
2074             version 20191206
2075              
2076             =head1 DESCRIPTION
2077              
2078             Handle the milter requests and pass off to individual handlers
2079              
2080             =head1 CONSTRUCTOR
2081              
2082             =head2 I<new( $thischild )>
2083              
2084             my $object = Mail::Milter::Authentication::Handler->new( $thischild );
2085              
2086             Takes the argument of the current Mail::Milter::Authentication object
2087             and creates a new handler object.
2088              
2089             =head1 METHODS
2090              
2091             =head2 I<get_version()>
2092              
2093             Return the version of this handler
2094              
2095             =head2 I<status( $status )>
2096              
2097             Set the status of the current child as visible by ps.
2098              
2099             =head2 I<config()>
2100              
2101             Return the configuration hashref.
2102              
2103             =head2 I<handler_config( $type )>
2104              
2105             Return the configuration for the current handler.
2106              
2107             =head2 I<handler_type()>
2108              
2109             Return the current handler type.
2110              
2111             =head2 I<set_return( $code )>
2112              
2113             Set the return code to be passed back to the MTA.
2114              
2115             =head2 I<get_return()>
2116              
2117             Get the current return code.
2118              
2119             =head2 I<get_reject_mail()>
2120              
2121             Get the reject mail reason (or undef)
2122              
2123             =head2 I<clear_reject_mail()>
2124              
2125             Clear the reject mail reason
2126              
2127             =head2 I<get_defer_mail()>
2128              
2129             Get the defer mail reason (or undef)
2130              
2131             =head2 I<clear_defer_mail()>
2132              
2133             Clear the defer mail reason
2134              
2135             =head2 I<get_quarantine_mail()>
2136              
2137             Get the quarantine mail reason (or undef)
2138              
2139             =head2 I<clear_quarantine_mail()>
2140              
2141             Clear the quarantine mail reason
2142              
2143             =head2 I<get_top_handler()>
2144              
2145             Return the current top Handler object.
2146              
2147             =head2 I<is_handler_loaded( $name )>
2148              
2149             Check if the named handler is loaded.
2150              
2151             =head2 I<get_handler( $name )>
2152              
2153             Return the named handler object.
2154              
2155             =head2 I<get_callbacks( $callback )>
2156              
2157             Return the list of handlers which have callbacks for the given event in the order they must be called in.
2158              
2159             =head2 I<set_object_maker( $name, $ref )>
2160              
2161             Register an object maker for type 'name'
2162              
2163             =head2 I<get_object( $name )>
2164              
2165             Return the named object from the object store.
2166              
2167             Object 'resolver' will be created if it does not already exist.
2168              
2169             Object 'spf_server' will be created by the SPF handler if it does not already exist.
2170              
2171             Handlers may register makers for other types as required.
2172              
2173             =head2 I<set_object( $name, $object, $destroy )>
2174              
2175             Store the given object in the object store with the given name.
2176              
2177             If $destroy then the object will be destroyed when the connection to the child closes
2178              
2179             =head2 I<destroy_object( $name )>
2180              
2181             Remove the reference to the named object from the object store.
2182              
2183             =head2 I<destroy_all_objects()>
2184              
2185             Remove the references to all objects currently stored in the object store.
2186              
2187             Certain objects (resolver and spf_server) are not destroyed for performance reasons.
2188              
2189             =head2 I<exit_on_close()>
2190              
2191             Exit this child once it has completed, do not process further requests with this child.
2192              
2193             =head2 I<reject_mail( $reason )>
2194              
2195             Reject mail with the given reason
2196              
2197             =head2 I<quarantine_mail( $reason )>
2198              
2199             Request quarantine mail with the given reason
2200              
2201             =head2 I<defer_mail( $reason )>
2202              
2203             Defer mail with the given reason
2204              
2205             =head2 I<clear_all_symbols()>
2206              
2207             Clear the symbol store.
2208              
2209             =head2 I<clear_symbols()>
2210              
2211             Clear the symbol store but do not remove the Connect symbols.
2212              
2213             =head2 I<set_symbol( $code, $key, $value )>
2214              
2215             Store the key value pair in the symbol store with the given code (event stage).
2216              
2217             =head2 I<get_symbol( $searchkey )>
2218              
2219             Return a value from the symbol store, searches all codes for the given key.
2220              
2221             =head2 I<tempfail_on_error()>
2222              
2223             Returns a TEMP FAIL to the calling MTA if the configuration is set to do so.
2224              
2225             Config can be set for all, authenticated, local, and trusted connections.
2226              
2227             =head2 I<can_sort_header( $header )>
2228              
2229             Returns 1 is this handler has a header_sort method capable or sorting entries for $header
2230             Returns 0 otherwise
2231              
2232             =head2 I<header_sort()>
2233              
2234             Sorting function for sorting the Authentication-Results headers
2235             Calls out to __HANDLER__->header_sort() to sort headers of a particular type if available,
2236             otherwise sorts alphabetically.
2237              
2238             =head2 I<add_headers()>
2239              
2240             Send the header changes to the MTA.
2241              
2242             =head2 I<prepend_header( $field, $value )>
2243              
2244             Add a trace header to the email.
2245              
2246             =head2 I<add_auth_header( $value )>
2247              
2248             Add a section to the authentication header for this email.
2249              
2250             =head2 I<add_c_auth_header( $value )>
2251              
2252             Add a section to the authentication header for this email, and to any subsequent emails for this connection.
2253              
2254             =head2 I<append_header( $field, $value )>
2255              
2256             Add a normal header to the email.
2257              
2258             =head1 METRICS METHODS
2259              
2260             =head2 I<get_json( $file )>
2261              
2262             Return json data from external file
2263              
2264             =head2 I<metric_register( $id, $help )>
2265              
2266             Register a metric type
2267              
2268             =head2 I<metric_count( $id, $labels, $count )>
2269              
2270             Increment a metrics counter by $count (defaults to 1 if undef)
2271              
2272             =head2 I<metric_send()>
2273              
2274             Send metrics to the parent
2275              
2276             =head2 I<register_metrics()>
2277              
2278             Return details of the metrics this module exports.
2279              
2280             =head1 RBL METHODS
2281              
2282             =head2 I<rbl_check_ip( $ip, $list )>
2283              
2284             Check the given IP address against an rbl list.
2285              
2286             Returns true is listed.
2287              
2288             =head2 I<rbl_check_domain( $domain, $list )>
2289              
2290             Check the given domain against an rbl list.
2291              
2292             Returns true is listed.
2293              
2294             =head1 TIMEOUT METHODS
2295              
2296             =head2 I<get_microseconds()>
2297              
2298             Return the current time in microseconds
2299              
2300             =head2 I<get_microseconds_since( $time )>
2301              
2302             Return the number of microseconds since the given time (in microseconds)
2303              
2304             =head2 I<is_exception_type( $exception )>
2305              
2306             Given a Mail::Milter::Authentication::Exception object, this return
2307             the exception object type.
2308             Otherwise returns undef.
2309              
2310             =head2 I<handle_exception( $exception )>
2311              
2312             Handle exceptions thrown, this method currently handles the
2313             timeout type, by re-throwing the exception.
2314              
2315             Should be called in Handlers when handling local exceptions, such that the
2316             higher level timeout exceptions are properly handled.
2317              
2318             =head2 I<get_time_remaining()>
2319              
2320             Return the time remaining (in microseconds) for the current Handler section level
2321             callback timeout.
2322              
2323             =head2 I<set_alarm( $microseconds )>
2324              
2325             Set a timeout alarm for $microseconds, and set the time remaining
2326             in the top level handler object.
2327              
2328             =head2 I<set_handler_alarm( $microseconds )>
2329              
2330             Set an alarm for $microseconds, or the current time remaining for the section callback, whichever
2331             is the lower. This should be used in Handler timeouts to ensure that a local timeout never goes for
2332             longer than the current handler section, or protocol section level timeout.
2333              
2334             =head2 I<reset_alarm()>
2335              
2336             Reset the alarm to the current time remaining in the section or protocol level timeouts.
2337              
2338             This should be called in Handlers after local timeouts have completed, to reset the higher level
2339             timeout alarm value.
2340              
2341             =head2 I<clear_overall_timeout()>
2342              
2343             Clear the current Handler level timeout, should be called from the Protocol layer, never from the Handler layer.
2344              
2345             =head2 I<set_overall_timeout( $microseconds )>
2346              
2347             Set the time in microseconds after which the Handler layer should timeout, called from the Protocol later, never from the Handler layer.
2348              
2349             =head2 I<get_type_timeout( $type )>
2350              
2351             For a given timeout type, return the configured timeout value, or the current handler level timeout, whichever is lower.
2352              
2353             =head2 I<check_timeout()>
2354              
2355             Manually check the current timeout, and throw if it has passed.
2356              
2357             =head1 CALLBACK METHODS
2358              
2359             =head2 I<top_setup_callback()>
2360              
2361             Top level handler for handler setup.
2362              
2363             =head2 I<remap_connect_callback( $hostname, $ip )>
2364              
2365             Top level handler for the connect event for remapping only.
2366              
2367             =head2 I<top_connect_callback( $hostname, $ip )>
2368              
2369             Top level handler for the connect event.
2370              
2371             =head2 I<remap_helo_callback( $helo_host )>
2372              
2373             Top level handler for the HELO event for remapping only.
2374              
2375             =head2 I<top_helo_callback( $helo_host )>
2376              
2377             Top level handler for the HELO event.
2378              
2379             =head2 I<top_envfrom_callback( $env_from )>
2380              
2381             Top level handler for the MAIL FROM event.
2382              
2383             =head2 I<top_envrcpt_callback( $env_to )>
2384              
2385             Top level handler for the RCPT TO event.
2386              
2387             =head2 I<top_header_callback( $header, $value, $original )>
2388              
2389             Top level handler for the BODY header event.
2390              
2391             =head2 I<top_eoh_callback()>
2392              
2393             Top level handler for the BODY end of headers event.
2394              
2395             =head2 I<top_body_callback( $body_chunk )>
2396              
2397             Top level handler for the BODY body chunk event.
2398              
2399             =head2 I<top_eom_callback()>
2400              
2401             Top level handler for the BODY end of message event.
2402              
2403             =head2 I<apply_policy()>
2404              
2405             Apply policy to the message, currently a nop.
2406              
2407             =head2 I<top_abort_callback()>
2408              
2409             Top level handler for the abort event.
2410              
2411             =head2 I<top_close_callback()>
2412              
2413             Top level handler for the close event.
2414              
2415             =head2 I<top_addheader_callback()>
2416              
2417             Top level handler for the add header event.
2418              
2419             Called after the Authentication-Results header has been added, but before any other headers.
2420              
2421             =head1 HELPER METHODS
2422              
2423             =head2 I<is_local_ip_address()>
2424              
2425             Is the current connection from a local ip address?
2426              
2427             Requires the LocalIP Handler to be loaded.
2428              
2429             =head2 I<is_trusted_ip_address()>
2430              
2431             Is the current connection from a trusted ip address?
2432              
2433             Requires the TrustedIP Handler to be loaded.
2434              
2435             =head2 I<is_encrypted()>
2436              
2437             Is the current connection encrypted?
2438              
2439             Requires the TLS Handler to be loaded.
2440              
2441             In SMTP mode this is only available AFTER the eoh call.
2442              
2443             Returns undef if the state is not yet known.
2444              
2445             =head2 I<is_authenticated()>
2446              
2447             Is the current connection authenticated?
2448              
2449             Requires the Auth Handler to be loaded.
2450              
2451             =head2 I<ip_address()>
2452              
2453             Return the ip address of the current connection.
2454              
2455             =head2 I<format_ctext( $text )>
2456              
2457             Format text as ctext for use in headers.
2458              
2459             Deprecated.
2460              
2461             =head2 I<format_ctext_no_space( $text )>
2462              
2463             Format text as ctext with no spaces for use in headers.
2464              
2465             Deprecated.
2466              
2467             =head2 I<format_header_comment( $comment )>
2468              
2469             Format text as a comment for use in headers.
2470              
2471             Deprecated.
2472              
2473             =head2 I<format_header_entry( $key, $value )>
2474              
2475             Format text as a key value pair for use in authentication header.
2476              
2477             Deprecated.
2478              
2479             =head2 I<get_domain_from( $address )>
2480              
2481             Extract a single domain from an email address.
2482              
2483             =head2 I<get_domains_from( $address )>
2484              
2485             Extract the domains from an email address as an arrayref.
2486              
2487             =head2 I<get_address_from( $text )>
2488              
2489             Extract a single email address from a string.
2490              
2491             =head2 I<get_addresses_from( $text )>
2492              
2493             Extract all email address from a string as an arrayref.
2494              
2495             =head2 I<get_my_hostname()>
2496              
2497             Return the effective hostname of the MTA.
2498              
2499             =head1 LOGGING METHODS
2500              
2501             =head2 I<dbgout( $key, $value, $priority )>
2502              
2503             Send output to debug and/or Mail Log.
2504              
2505             priority is a standard Syslog priority.
2506              
2507             =head2 I<log_error( $error )>
2508              
2509             Log an error.
2510              
2511             =head2 I<dbgoutwrite()>
2512              
2513             Write out logs to disc.
2514              
2515             Logs are not written immediately, they are written at the end of a connection so we can
2516             include a queue id. This is not available at the start of the process.
2517              
2518             =head1 LOW LEVEL METHODS
2519              
2520             =head2 I<smfis_continue()>
2521              
2522             Return Continue code.
2523              
2524             =head2 I<smfis_tempfail()>
2525              
2526             Return TempFail code.
2527              
2528             =head2 I<smfis_reject()>
2529              
2530             Return Reject code.
2531              
2532             =head2 I<smfis_discard()>
2533              
2534             Return Discard code.
2535              
2536             =head2 I<smfis_accept()>
2537              
2538             Return Accept code.
2539              
2540             =head2 I<write_packet( $type, $data )>
2541              
2542             Write a packet to the MTA (calls Protocol object)
2543              
2544             =head2 I<add_header( $key, $value )>
2545              
2546             Write an Add Header packet to the MTA (calls Protocol object)
2547              
2548             =head2 I<insert_header( $index, $key, $value )>
2549              
2550             Write an Insert Header packet to the MTA (calls Protocol object)
2551              
2552             =head2 I<change_header( $key, $index, $value )>
2553              
2554             Write a Change Header packet to the MTA (calls Protocol object)
2555              
2556             =head1 WRITING HANDLERS
2557              
2558             tbc
2559              
2560             =head1 AUTHOR
2561              
2562             Marc Bradshaw <marc@marcbradshaw.net>
2563              
2564             =head1 COPYRIGHT AND LICENSE
2565              
2566             This software is copyright (c) 2018 by Marc Bradshaw.
2567              
2568             This is free software; you can redistribute it and/or modify it under
2569             the same terms as the Perl 5 programming language system itself.
2570              
2571             =cut