File Coverage

blib/lib/Mail/Milter/Authentication.pm
Criterion Covered Total %
statement 517 770 67.1
branch 115 234 49.1
condition 30 130 23.0
subroutine 52 61 85.2
pod 39 40 97.5
total 753 1235 60.9


line stmt bran cond sub pod time code
1             package Mail::Milter::Authentication;
2 127     127   1720 use 5.20.0;
  127         507  
3 127     127   712 use strict;
  127         362  
  127         3144  
4 127     127   744 use warnings;
  127         268  
  127         3053  
5 127     127   660 use Mail::Milter::Authentication::Pragmas;
  127         580  
  127         672  
6             # ABSTRACT: A Perl Mail Authentication Milter
7             our $VERSION = '3.20230911'; # VERSION
8 127     127   123433 use Mail::Milter::Authentication::Handler;
  127         597  
  127         6330  
9 127     127   77296 use Mail::Milter::Authentication::Metric;
  127         438  
  127         5024  
10 127     127   66840 use Mail::Milter::Authentication::Protocol::Milter;
  127         464  
  127         4368  
11 127     127   79122 use Mail::Milter::Authentication::Protocol::SMTP;
  127         460  
  127         5502  
12 127     127   59865 use Email::Sender::Simple qw(try_to_sendmail);
  127         18598546  
  127         912  
13 127     127   48750 use Email::Simple::Creator;
  127         482  
  127         8017  
14 127     127   718 use Email::Simple;
  127         6032  
  127         2809  
15 127     127   64985 use ExtUtils::Installed;
  127         13596563  
  127         5056  
16 127     127   75031 use Log::Dispatchouli;
  127         29356999  
  127         5864  
17 127     127   68422 use Net::DNS 1.01;
  127         321973  
  127         12180  
18 127     127   1255 use Net::DNS::Resolver;
  127         557  
  127         2574  
19 127     127   832 use Net::IP;
  127         595  
  127         19043  
20 127     127   1112 use Proc::ProcessTable;
  127         286  
  127         5823  
21 127     127   1009 use base 'Mail::Milter::Authentication::Net::ServerPatches';
  127         355  
  127         73002  
22 127     127   1144 use vars qw(@ISA);
  127         331  
  127         1317587  
23              
24              
25             {
26             my $LOGGER;
27             sub logger {
28              
29 15280 100   15280 0 96280 return $LOGGER if $LOGGER;
30 49         508 my $config = get_config();
31              
32 49         1864 my $MYARGS = {
33             'ident' => $Mail::Milter::Authentication::Config::IDENT,
34             'to_stderr' => 0, # handled elsewhere
35             'log_pid' => 1,
36             'facility' => LOG_MAIL,
37             };
38 49 50       674 if ( exists $config->{ 'log_dispatchouli' } ) {
39 0         0 $MYARGS = $config->{ 'log_dispatchouli' };
40             }
41              
42 49         3030 $LOGGER = Log::Dispatchouli->new( $MYARGS );
43 49         4259144 $LOGGER->log('Logging instantiated');
44 49         63542 return $LOGGER;
45             }
46             }
47              
48 15658     15658   27229 sub _warn($msg) {
  15658         26909  
  15658         22376  
49 15658         49577 my @parts = split "\n", $msg;
50 15658         33467 foreach my $part ( @parts ) {
51 16490 50       37000 next if $part eq q{};
52 16490         856761 print STDERR scalar(localtime) . ' ' . $Mail::Milter::Authentication::Config::IDENT . "[$PID] $part\n";
53             }
54             }
55              
56              
57 37     37 1 164 sub preload_modules($self,$from,$matching) {
  37         395  
  37         326  
  37         330  
  37         102  
58 37         2290 my $installed = ExtUtils::Installed->new( 'skip_cwd' => 1 );
59 37         24312535 my $path_matching = $matching;
60 37         1320 $path_matching =~ s/::/\//g;
61 37         3381 foreach my $module ( grep { /$from/ } $installed->modules() ) {
  11544         48428  
62             FILE:
63 148         120920 foreach my $file ( grep { /$path_matching\/\w+\.pm$/ } $installed->files( $module ) ) {
  3256         237519  
64 2294 50       4429947 next FILE if ! -e $file;
65 2294         16663 my ( $module ) = reverse split '/', $file;
66 2294         16264 $module =~ s/\.pm$//;
67 2294         7322 $module = join( '::', $matching, $module );
68 2294 100       8737 if ( ! is_loaded( $module ) ) {
69 1950         53507 $self->logdebug( "Preloading Module $module" );
70 1950         6453 load $module;
71             }
72             else {
73 344         9591 $self->logdebug( "Preloading Module $module already loaded" );
74             }
75             }
76             }
77             }
78              
79              
80             sub get_installed_handlers {
81 0     0 1 0 my @installed_handlers;
82 0         0 my $installed = ExtUtils::Installed->new( 'skip_cwd' => 1 );
83 0         0 foreach my $module ( grep { /Mail::Milter::Authentication/ } $installed->modules() ) {
  0         0  
84             FILE:
85 0         0 foreach my $file ( grep { /Mail\/Milter\/Authentication\/Handler\/\w+\.pm$/ } $installed->files( $module ) ) {
  0         0  
86 0 0       0 next FILE if ! -e $file;
87 0         0 my ( $handler ) = reverse split '/', $file;
88 0         0 $handler =~ s/\.pm$//;
89 0         0 push @installed_handlers, $handler;
90             }
91             }
92 0         0 return \@installed_handlers;
93             }
94              
95              
96 229     229 1 1181605 sub write_to_log_hook($self,$priority,$line,@) {
  229         699  
  229         557  
  229         623  
  229         485  
97 229 0       1701 my $log_priority = $priority == 0 ? 'debug'
    0          
    50          
    100          
    100          
98             : $priority == 1 ? 'info'
99             : $priority == 2 ? 'notice'
100             : $priority == 3 ? 'warning'
101             : $priority == 4 ? 'error'
102             : 'debug';
103 229         1051 logger()->log( { 'level' => $log_priority }, $line );
104             }
105              
106              
107 260     260 1 761654198 sub idle_loop_hook($self,@) {
  260         1288  
  260         993  
108 260         4054 $self->{'metric'}->parent_metric_update( $self );
109             }
110              
111              
112 37     37 1 75595 sub pre_loop_hook($self,@) {
  37         239  
  37         100  
113 37         1004 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':parent';
114              
115 37         547 $self->preload_modules( 'Net::DNS', 'Net::DNS::RR' );
116 37         4503 $self->{'metric'} = Mail::Milter::Authentication::Metric->new($self);
117              
118             # Load handlers
119 37         322 my $config = get_config();
120 37         473 $self->{'config'} = $config;
121 37         226 foreach my $name ( @{$config->{'load_handlers'}} ) {
  37         534  
122 333         20825 $self->load_handler( $name );
123              
124 333         3548 my $package = "Mail::Milter::Authentication::Handler::$name";
125 333         4270 my $object = $package->new( $self );
126 333 100       3617 if ( $object->can( 'pre_loop_setup' ) ) {
127 74         402 $object->pre_loop_setup();
128             }
129 333 50       45620 if ( $object->can( 'register_metrics' ) ) {
130 333         1680 $self->{'metric'}->register_metrics( $object->register_metrics() );
131             }
132              
133             }
134              
135 37         2971 $self->{'metric'}->register_metrics( {
136             'forked_children_total' => 'Total number of child processes forked',
137             'reaped_children_total' => 'Total number of child processes reaped',
138             'dequeue_files_total' => { help => 'The number of dequeue files queued', type => 'gauge' },
139             } );
140              
141 37         2515 $self->{'metric'}->register_metrics( Mail::Milter::Authentication::Handler->register_metrics() );
142              
143 37 100       2097 if ( $config->{'protocol'} eq 'milter' ) {
    50          
144 20         1016 $self->{'metric'}->register_metrics( Mail::Milter::Authentication::Protocol::Milter->register_metrics() );
145             }
146             elsif ( $config->{'protocol'} eq 'smtp' ) {
147 17         912 $self->{'metric'}->register_metrics( Mail::Milter::Authentication::Protocol::SMTP->register_metrics() );
148             }
149             else {
150 0         0 die "Unknown protocol " . $config->{'protocol'} . "\n";
151             }
152              
153 37 50       2060 if ( $config->{'error_log'} ) {
154 37 50       2438 open( STDERR, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
155 37 50       1915 open( STDOUT, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
156             }
157             }
158              
159              
160 39     39 1 17198 sub run_n_children_hook($self,@) {
  39         151  
  39         87  
161             # Load handlers
162 39         223 my $config = get_config();
163 39         175 $self->{'config'} = $config;
164 39         511 $self->{metric}->re_register_metrics;
165 39         1984 foreach my $name ( @{$config->{'load_handlers'}} ) {
  39         568  
166              
167 335         921 my $package = "Mail::Milter::Authentication::Handler::$name";
168 335         3244 my $object = $package->new( $self );
169 335 100       2711 if ( $object->can( 'pre_fork_setup' ) ) {
170 33         229 $object->pre_fork_setup();
171             }
172              
173             }
174             }
175              
176              
177             sub child_init_hook {
178 26     26 1 67514 my ( $self,$arg ) = @_;
179 26         1611 srand();
180              
181 26         1741 my $config = get_config();
182 26         4388 $self->{'config'} = $config;
183              
184 26 50       1130 if ( $config->{'error_log'} ) {
185 26         576 eval {
186 26 50       3718 open( STDERR, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
187 26 50       2456 open( STDOUT, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
188             };
189 26 50       750 if ( my $error = $@ ) {
190 0         0 $self->logerror( "Child process $PID could not open the error log: $error" );
191             }
192             }
193              
194 26 100       1024 $arg = '' if !defined $arg;
195 26 100       5016 if ( $arg eq 'dequeue' ) {
196 3         325 $self->loginfo( "Dequeue process $PID starting up" );
197 3         10627 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':dequeue';
198             }
199             else {
200 23         1786 $self->loginfo( "Child process $PID starting up" );
201 23         69321 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':starting';
202             }
203              
204 26         359 my $base;
205 26 100       849 if ( $config->{'protocol'} eq 'milter' ) {
    50          
206 14         327 $base = 'Mail::Milter::Authentication::Protocol::Milter';
207             }
208             elsif ( $config->{'protocol'} eq 'smtp' ) {
209 12         278 $base = 'Mail::Milter::Authentication::Protocol::SMTP';
210             }
211             else {
212 0         0 die "Unknown protocol " . $config->{'protocol'} . "\n";
213             }
214 26         4126 push @ISA, $base;
215              
216             # BEGIN MILTER PROTOCOL BLOCK
217 26 100       964 if ( $config->{'protocol'} eq 'milter' ) {
218 14         373 my $protocol = SMFIP_NONE & ~(SMFIP_NOCONNECT|SMFIP_NOMAIL);
219 14         247 $protocol &= ~SMFIP_NOHELO;
220 14         182 $protocol &= ~SMFIP_NORCPT;
221 14         228 $protocol &= ~SMFIP_NOBODY;
222 14         200 $protocol &= ~SMFIP_NOHDRS;
223 14         173 $protocol &= ~SMFIP_NOEOH;
224 14         196 $protocol |= SMFIP_HDR_LEADSPC;
225 14         752 $self->{'protocol'} = $protocol;
226 14         244 $self->{'headers_include_space'} = 0;
227              
228 14         266 my $callback_flags = SMFI_CURR_ACTS|SMFIF_CHGBODY|SMFIF_QUARANTINE|SMFIF_SETSENDER;
229 14         406 $self->{'callback_flags'} = $callback_flags;
230             }
231             # END MILTER PROTOCOL BLOCK
232              
233 26         494 my $callbacks_list = {};
234 26         457 my $callbacks = {};
235 26         496 my $handler = {};
236 26         626 my $object = {};
237 26         498 my $object_maker = {};
238 26         281 my $count = 0;
239              
240 26         1142 $self->{'callbacks_list'} = $callbacks_list;
241 26         721 $self->{'callbacks'} = $callbacks;
242 26         716 $self->{'count'} = $count;
243 26         432 $self->{'handler'} = $handler;
244 26         651 $self->{'object'} = $object;
245 26         508 $self->{'object_maker'} = $object_maker;
246              
247 26         1091 $self->setup_handlers();
248 26         1330 $self->{'metric'}->set_versions( $self );
249              
250 26 100       381 $self->{'handler'}->{'_Handler'}->metric_count( 'forked_children_total', {}, 1 ) unless $arg eq 'dequeue';
251              
252 26         433 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':waiting(0)';
253             }
254              
255              
256             sub child_finish_hook {
257 25     25 1 2741562 my ( $self,$arg ) = @_;
258 25         2056 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':exiting';
259 25 100       454 $arg = '' if !defined $arg;
260 25 100       236 if ( $arg eq 'dequeue' ) {
261 3         29 $self->loginfo( "Dequeue process $PID shutting down" );
262             }
263             else {
264 22         374 $self->loginfo( "Child process $PID shutting down" );
265 22         23120 eval {
266 22         245 $self->{'handler'}->{'_Handler'}->metric_count( 'reaped_children_total', {}, 1 );
267             };
268             }
269 25         2250 $self->destroy_objects();
270             }
271              
272              
273 11     11 1 56655 sub pre_server_close_hook($self,@) {
  11         265  
  11         151  
274 11         2355 $self->loginfo( 'Server closing down' );
275             }
276              
277              
278 3     3 1 31 sub dequeue($self,@) {
  3         23  
  3         22  
279 3         15 my $config = $self->{ 'config' };
280 3   50     96 my $seconds = $config->{'dequeue_timeout'} // 300;
281 3         116 $self->{handler}->{_Handler}->set_overall_timeout( $seconds * 1000000 );
282 3         66 $self->{handler}->{_Handler}->top_dequeue_callback();
283 3         29 $self->{handler}->{_Handler}->clear_overall_timeout();
284             }
285              
286              
287 244     244 1 756 sub get_client_proto($self,@) {
  244         751  
  244         634  
288 244         874 my $socket = $self->{server}{client};
289 244 50       3878 if ($socket->isa("Net::Server::Proto")) {
290 0         0 my $proto = $socket->NS_proto;
291 0 0       0 $proto = "UNIX" if $proto =~ m/^UNIX/;
292 0         0 return $proto;
293             }
294              
295 244 50       4878 if ($socket->isa("IO::Socket::IP")) {
296 0         0 return "TCP";
297             }
298              
299 244 50       2164 if ($socket->isa("IO::Socket::INET")) {
300 0         0 return "TCP";
301             }
302              
303 244 50       2044 if ($socket->isa("IO::Socket::INET6")) {
304 0         0 return "TCP";
305             }
306              
307 244 50       1206 if ($socket->isa("IO::Socket::UNIX")) {
308 244         2298 return "UNIX";
309             }
310              
311 0         0 $self->logerror( "Could not determine connection protocol: " . ref($socket) );
312             }
313              
314              
315 0     0 1 0 sub get_client_port($self,@) {
  0         0  
  0         0  
316 0         0 my $socket = $self->{server}{client};
317 0         0 return $socket->sockport();
318             }
319              
320              
321 0     0 1 0 sub get_client_host($self,@) {
  0         0  
  0         0  
322 0         0 my $socket = $self->{server}{client};
323 0         0 return $socket->sockhost();
324             }
325              
326              
327 244     244 1 670 sub get_client_path($self,@) {
  244         562  
  244         523  
328 244         763 my $socket = $self->{server}{client};
329 244         2197 return $socket->hostpath();
330             }
331              
332              
333 163     163 1 471 sub get_client_details($self,@) {
  163         352  
  163         354  
334 163         680 my $proto = lc $self->get_client_proto();
335 163 50       1078 if ( $proto eq 'tcp' ) {
    50          
336 0         0 return 'inet:' . $self->get_client_port();
337             }
338             elsif ( $proto eq 'unix' ) {
339 163         652 return 'unix:' . $self->get_client_path();
340             }
341             }
342              
343              
344 81     81 1 481866774 sub process_request($self,@) {
  81         421  
  81         272  
345 81         523 my $config = $self->{'config'};
346              
347 81         595 my $metric_type;
348             my $metric_path;
349 81         0 my $metric_host;
350              
351 81 50       741 if ( defined( $config->{'metric_connection'} ) ) {
352 81         409 my $connection = $config->{'metric_connection'};
353 81         435 my $umask = $config->{'metric_umask'};
354              
355 81         1571 $connection =~ /^([^:]+):([^:@]+)(?:@([^:@]+|\[[0-9a-f:\.]+\]))?$/;
356 81         994 $metric_type = $1;
357 81         737 $metric_path = $2;
358 81   50     1392 $metric_host = $3 || q{};
359             }
360              
361             ## ToDo, match also on client_host
362              
363             # Legacy metrics
364 81 50 33     6605 if ( defined( $config->{ 'metric_port' } ) && $self->get_client_proto() eq 'TCP' && $self->get_client_port() eq $config->{'metric_port'} ) {
    50 33        
    100 33        
      33        
      33        
      33        
      33        
      66        
365 0         0 $self->{'metric'}->child_handler( $self );
366             }
367              
368             elsif ( defined( $config->{ 'metric_connection' } ) && $metric_type eq 'inet' && $self->get_client_proto eq 'TCP' && $self->get_client_port() eq $metric_path ) {
369 0         0 $self->{'metric'}->child_handler( $self );
370             }
371              
372             elsif ( defined( $config->{ 'metric_connection' } ) && $metric_type eq 'unix' && $self->get_client_proto eq 'UNIX' && $self->get_client_path() eq $metric_path ) {
373 11         1509 $self->{'metric'}->child_handler( $self );
374             }
375              
376             else {
377 70         6465 $self->process_main();
378             }
379              
380 80         437 my $count = $self->{'count'};
381 80         1324 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':waiting(' . $count . ')';
382             }
383              
384              
385 70     70 1 518 sub process_main($self,@) {
  70         291  
  70         302  
386 70         378 $self->{'count'}++;
387 70         296 my $count = $self->{'count'};
388 70         391 my $config = $self->{'config'};
389              
390 70         2216 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':processing(' . $count . ')';
391 70         1020 $self->logdebug( 'Processing request ' . $self->{'count'} );
392 70         583 $self->{'socket'} = $self->{'server'}->{'client'};
393              
394 70         557 $self->{'tracelog'} = [];
395              
396 70         1753 $self->protocol_process_request();
397              
398             # Call close callback
399 70         584 $self->{'handler'}->{'_Handler'}->top_close_callback();
400 70 100       499 if ( $self->{'handler'}->{'_Handler'}->{'exit_on_close'} ) {
401 1   50     9 my $error = $self->{'handler'}->{'_Handler'}->{'exit_on_close_error'} // 'no reason given';
402 1         9 $self->send_exception_email($error);
403 1         22 $self->fatal('exit_on_close requested - '.$error);
404             }
405              
406 69         6709 $self->{'tracelog'} = [];
407              
408 69 50       489 if ( $config->{'debug'} ) {
409 0         0 my $process_table = Proc::ProcessTable->new();
410 0         0 foreach my $process ( @{$process_table->table} ) {
  0         0  
411 0 0       0 if ( $process->pid == $PID ) {
412 0         0 my $size = $process->size;
413 0         0 my $rss = $process->rss;
414 0         0 my $pctmem = $process->pctmem;
415 0         0 my $pctcpu = $process->pctcpu;
416 0         0 $self->loginfo( "Resource usage: ($count) size $size/rss $rss/memory $pctmem\%/cpu $pctcpu\%" );
417             }
418             }
419             }
420              
421 69         255 delete $self->{'handler'}->{'_Handler'}->{'reject_mail'};
422 69         242 delete $self->{'handler'}->{'_Handler'}->{'return_code'};
423 69         215 delete $self->{'socket'};
424 69         338 $self->logdebug( 'Request processing completed' );
425             }
426              
427              
428             sub send_exception_email {
429 1     1 1 6 my ( $self, $error ) = @_;
430              
431 1 50       6 if ( $self->{'handler'}->{'_Handler'}->{'suppress_error_emails'} ) {
432 0         0 return;
433             }
434              
435 1         9 my $config = get_config();
436 1   50     11 my $errors_to = $config->{'errors_to'} || return;
437 0   0     0 my $errors_from = $config->{'errors_from'} || return;
438 0   0     0 my $errors_headers = $config->{'errors_headers'} || {};
439              
440 0         0 my $email = "Authentication Milter " . $Mail::Milter::Authentication::Config::IDENT . " Error\n\n";
441 0         0 $email .= "$error\n\n";
442              
443 0         0 $email .= "Child PID: $PID\n\n";
444              
445 0         0 $email .= "Log:\n";
446 0         0 $email .= join( "\n", $self->{'tracelog'}->@* );
447 0         0 $email .= "\n\n";
448              
449 0         0 $email .= "Processes Running\n";
450 0         0 my $ppid = $self->{'server'}->{'ppid'};
451 0         0 my $process_table = Proc::ProcessTable->new;
452 0         0 foreach my $process ( $process_table->table->@* ) {
453 0 0 0     0 next if ! ( $process->pid == $ppid || $process->ppid == $ppid );
454 0   0     0 my $pid = eval{ $process->pid } // '';
  0         0  
455 0   0     0 my $cmndline = eval{ $process->cmndline } // '';
  0         0  
456 0   0     0 my $size = eval{ $process->size } // '';
  0         0  
457 0   0     0 my $rss = eval{ $process->rss } // '';
  0         0  
458 0   0     0 my $pctmem = eval{ $process->pctmem } // '';
  0         0  
459 0   0     0 my $pctcpu = eval{ $process->pctcpu } // '';
  0         0  
460 0         0 $email .= "pid:$pid, $cmndline, size:$size, mem:$rss ($pctmem%), cpu: $pctcpu%\n";
461             }
462              
463 0         0 my @headers;
464 0         0 foreach my $key ( sort keys $errors_headers->%* ) {
465 0         0 push @headers, $key => $errors_headers->{$key};
466             }
467 0         0 push @headers, to => $errors_to;
468 0         0 push @headers, From => $errors_from;
469 0         0 push @headers, Subject => 'Authentication Milter Error';
470 0         0 push @headers, 'X-Authentication-Milter-Error' => 'Generated Error Report';
471              
472 0         0 my $emailer = Email::Simple->create(
473             header => \@headers,
474             body => $email,
475             );
476 0         0 try_to_sendmail($emailer);
477              
478             }
479              
480              
481             sub send_panic_email {
482 0     0 1 0 my ( $error ) = @_;
483              
484 0         0 my $config = get_config();
485 0   0     0 my $errors_to = $config->{'errors_to'} || return;
486 0   0     0 my $errors_from = $config->{'errors_from'} || return;
487 0   0     0 my $errors_headers = $config->{'errors_headers'} || {};
488              
489 0         0 my $email = "Authentication Milter " . $Mail::Milter::Authentication::Config::IDENT . " Error\n\n";
490 0         0 $email .= "$error\n\n";
491              
492 0         0 $email .= "PID: $PID\n\n";
493              
494 0         0 $email .= "Processes Running\n";
495 0         0 my $process_table = Proc::ProcessTable->new;
496 0         0 foreach my $process ( $process_table->table->@* ) {
497 0 0 0     0 next if ! ( $process->pid == $PID || $process->ppid == $PID );
498 0   0     0 my $pid = eval{ $process->pid } // '';
  0         0  
499 0   0     0 my $cmndline = eval{ $process->cmndline } // '';
  0         0  
500 0   0     0 my $size = eval{ $process->size } // '';
  0         0  
501 0   0     0 my $rss = eval{ $process->rss } // '';
  0         0  
502 0   0     0 my $pctmem = eval{ $process->pctmem } // '';
  0         0  
503 0   0     0 my $pctcpu = eval{ $process->pctcpu } // '';
  0         0  
504 0         0 $email .= "pid:$pid, $cmndline, size:$size, mem:$rss ($pctmem%), cpu: $pctcpu%\n";
505             }
506              
507 0         0 my @headers;
508 0         0 foreach my $key ( sort keys $errors_headers->%* ) {
509 0         0 push @headers, $key => $errors_headers->{$key};
510             }
511 0         0 push @headers, to => $errors_to;
512 0         0 push @headers, From => $errors_from;
513 0         0 push @headers, Subject => 'Authentication Milter Error';
514 0         0 push @headers, 'X-Authentication-Milter-Error' => 'Generated Error Report';
515              
516 0         0 my $emailer = Email::Simple->create(
517             header => \@headers,
518             body => $email,
519             );
520 0         0 try_to_sendmail($emailer);
521              
522             }
523              
524              
525 0     0 1 0 sub get_valid_pid($pid_file) {
  0         0  
  0         0  
526 0 0       0 if ( ! $pid_file ) {
527 0         0 return undef; ## no critic
528             }
529 0 0       0 if ( ! -e $pid_file ) {
530 0         0 return undef; ## no critic
531             }
532              
533 0   0     0 open my $inf, '<', $pid_file || return undef; ## no critic
534 0         0 my $pid = <$inf>;
535 0         0 close $inf;
536              
537 0         0 my $self_pid = $PID;
538 0         0 my $found_self = 0;
539 0         0 my $found_pid = 0;
540              
541 0         0 my $process_table = Proc::ProcessTable->new();
542 0         0 foreach my $process ( $process_table->table->@* ) {
543 0 0       0 if ( $process->pid == $self_pid ) {
544 0 0       0 if ( $process->cmndline eq $Mail::Milter::Authentication::Config::IDENT . ':control' ) {
545 0         0 $found_self = 1;
546             }
547             }
548 0 0       0 if ( $process->pid == $pid ) {
549 0         0 $found_pid = 1;
550 0 0       0 if ( $process->cmndline eq $Mail::Milter::Authentication::Config::IDENT . ':parent' ) {
551 0         0 return $pid;
552             }
553             }
554             }
555              
556             # If we didn't find ourself in the process table then we can assume that
557             # $0 is read only on our current operating system, and return the pid that we read from the
558             # pidfile if it is in the process table regardness of it's process name..
559 0 0       0 if ( ! $found_self ) {
560 0 0       0 if ( $found_pid ) {
561 0         0 return $pid;
562             }
563             }
564              
565 0         0 return undef; ## no critic
566             }
567              
568              
569             sub find_process {
570 0     0 1 0 my $process_table = Proc::ProcessTable->new();
571 0         0 foreach my $process ( $process_table->table->@* ) {
572 0 0       0 if ( $process->cmndline eq $Mail::Milter::Authentication::Config::IDENT . ':master' ) { ## Legacy naming, will be removed in later version
573 0         0 return $process->pid;
574             }
575 0 0       0 if ( $process->cmndline eq $Mail::Milter::Authentication::Config::IDENT . ':parent' ) {
576 0         0 return $process->pid;
577             }
578             }
579 0         0 return undef; ## no critic
580             }
581              
582              
583 0     0 1 0 sub control($args) {
  0         0  
  0         0  
584 0         0 my $pid_file = $args->{'pid_file'};
585 0         0 my $command = $args->{'command'};
586              
587 0         0 my $OriginalProgramName = $PROGRAM_NAME;
588 0         0 $PROGRAM_NAME = $Mail::Milter::Authentication::Config::IDENT . ':control';
589              
590 0 0 0     0 if ( $command eq 'stop' ) {
    0          
    0          
591 0   0     0 my $pid = get_valid_pid( $pid_file ) || find_process();
592 0 0       0 if ( $pid ) {
593 0         0 print "Process found, stopping\n";
594 0         0 kill 'QUIT', $pid;
595             }
596             else {
597 0         0 print "No process found\n";
598             }
599             }
600             elsif ( $command eq 'restart' || $command eq 'start' ) {
601 0   0     0 my $pid = get_valid_pid( $pid_file ) || find_process();
602 0 0       0 if ( $pid ) {
603 0         0 print "Process found, restarting\n";
604 0         0 kill 'HUP', $pid;
605             }
606             else {
607 0         0 print "No process found, starting up\n";
608 0         0 $PROGRAM_NAME = $OriginalProgramName;
609 0         0 start({
610             'pid_file' => $pid_file,
611             'daemon' => 1,
612             });
613             }
614             }
615             elsif ( $command eq 'status' ) {
616 0   0     0 my $pid = get_valid_pid( $pid_file ) || find_process();
617 0 0       0 if ( $pid ) {
618 0         0 print "Process running with pid $pid\n";
619 0 0       0 if ( ! get_valid_pid( $pid_file ) ) {
620 0         0 print "pid file $pid_file is invalid\n";
621             }
622             }
623             else {
624 0         0 print "No process found\n";
625             }
626             }
627             else {
628 0         0 die 'unknown command';
629             }
630             }
631              
632              
633 37     37 1 272 sub start($args) {
  37         406  
  37         119  
634             local $SIG{__WARN__} = sub {
635 136     136   696016 foreach my $msg ( @_ ) {
636 136         774 logger()->log( { level => 'warning' }, "Warning: $msg" );
637 136         125209 _warn( "Warning: $msg" );
638             }
639 37         2186 };
640              
641 37         4268 my $config = get_config();
642              
643 37   50     643 my $default_connection = $config->{'connection'} || die('No connection details given');
644              
645 37         330 my $pid_file = $args->{'pid_file'};
646              
647 37   50     343 my $listen_backlog = $config->{'listen_backlog'} || 20;
648 37   50     433 my $max_children = $config->{'max_children'} || 100;
649 37   50     453 my $max_requests_per_child = $config->{'max_requests_per_child'} || 200;
650 37   50     274 my $min_children = $config->{'min_children'} || 20;
651 37   50     269 my $max_spare_children = $config->{'max_spare_children'} || 20;
652 37   50     312 my $min_spare_children = $config->{'min_spare_children'} || 10;
653              
654 37         300 setup_config();
655              
656 37         188 my %srvargs;
657              
658 37         892 $srvargs{'no_client_stdout'} = 1;
659              
660             # Early redirection to log file if possible
661 37 50       569 if ( $config->{'error_log'} ) {
662 37 50       7013 open( STDERR, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
663 37 50       2097 open( STDOUT, '>>', $config->{'error_log'} ) || die "Cannot open errlog [$!]";
664             }
665              
666 37 50       327 if ( $args->{'daemon'} ) {
667 0 0       0 if ( $EUID == 0 ) {
668 0         0 _warn(
669             join( ' ',
670             'daemonize',
671             "servers=$min_children/$max_children",
672             "spares=$min_spare_children/$max_spare_children",
673             "requests=$max_requests_per_child",
674             )
675             );
676 0         0 $srvargs{'background'} = 1;
677 0         0 $srvargs{'setsid'} = 1;
678             }
679             else {
680 0         0 _warn("Not running as root, daemonize ignored!");
681             }
682             }
683 37         301 $srvargs{'pid_file'} = $pid_file;
684 37         451 $srvargs{'max_servers'} = $max_children;
685 37         3282 $srvargs{'max_requests'} = $max_requests_per_child;
686 37         582 $srvargs{'min_servers'} = $min_children;
687 37         199 $srvargs{'min_spare_servers'} = $min_spare_children;
688 37         226 $srvargs{'max_spare_servers'} = $max_spare_children;
689              
690 37 50       264 $srvargs{'lock_file'} = $config->{'lock_file'} if $config->{'lock_file'};
691              
692 37 50       440 if ( $EUID == 0 ) {
693 37         288 my $user = $config->{'runas'};
694 37         152 my $group = $config->{'rungroup'};
695 37 50 33     400 if ( $user && $group ) {
696 0         0 _warn("run as user=$user group=$group");
697 0         0 $srvargs{'user'} = $user;
698 0         0 $srvargs{'group'} = $group;
699             }
700             else {
701 37         1357 _warn("No runas details supplied, could not drop privs - be careful!");
702             }
703             # Note, Chroot requires a chroot environment which is out of scope at present
704 37 50       420 if ( $config->{'error_log'} ) {
705 37 50       779 if ( ! -e $config->{'error_log'} ) {
706 0   0     0 open my $outf, '>', $config->{'error_log'} || die "Could not create error log: $!\n";;
707 0         0 close $outf;
708             }
709 37 50       293 if ( $user ) {
710 0         0 my ($login,$pass,$uid,$gid) = getpwnam($user);
711 0         0 chown $uid, $gid, $config->{'error_log'};
712             }
713             }
714 37 50       297 if ( exists( $config->{'chroot'} ) ) {
715 0         0 _warn('Chroot to ' . $config->{'chroot'});
716 0         0 $srvargs{'chroot'} = $config->{'chroot'};
717             }
718             }
719             else {
720 0         0 _warn("Not running as root, could not drop privs - be careful!");
721             }
722              
723 37         525 my $connections = {};
724              
725 37 50       634 if ( exists $config->{'connections'} ) {
726 0         0 $connections = $config->{'connections'};
727             }
728              
729             $connections->{'default'} = {
730             'connection' => $default_connection,
731 37         766 'umask' => $config->{'umask'},
732             };
733              
734 37         297 my @ports;
735 37         626 foreach my $key ( keys %$connections ) {
736 37         410 my $connection = $connections->{$key}->{'connection'};
737 37         225 my $umask = $connections->{$key}->{'umask'};
738              
739 37         602 $connection =~ /^([^:]+):([^:@]+)(?:@([^:@]+|\[[0-9a-f:\.]+\]))?$/;
740 37         317 my $type = $1;
741 37         620 my $path = $2;
742 37   50     996 my $host = $3 || q{};
743 37 50       599 if ( $type eq 'inet' ) {
    50          
744 0         0 _warn(
745             join( ' ',
746             'listening on inet',
747             "host=$host",
748             "port=$path",
749             "backlog=$listen_backlog",
750             )
751             );
752 0         0 push @ports, {
753             'host' => $host,
754             'port' => $path,
755             'ipv' => '*',
756             'proto' => 'tcp',
757             };
758             }
759             elsif ( $type eq 'unix' ) {
760 37         554 _warn(
761             join( ' ',
762             'listening on unix',
763             "socket=$path",
764             "backlog=$listen_backlog",
765             )
766             );
767 37         1177 push @ports, {
768             'port' => $path,
769             'proto' => 'unix',
770             };
771              
772 37 50       325 if ($umask) {
773 37         531 umask ( oct( $umask ) );
774 37         300 _warn( 'setting umask to ' . $umask );
775             }
776              
777             }
778             else {
779 0         0 die 'Invalid connection';
780             }
781             }
782              
783 37 50       572 if ( defined( $config->{'metric_connection'} ) ) {
    0          
784 37         303 my $connection = $config->{'metric_connection'};
785 37         250 my $umask = $config->{'metric_umask'};
786              
787 37         579 $connection =~ /^([^:]+):([^:@]+)(?:@([^:@]+|\[[0-9a-f:\.]+\]))?$/;
788 37         393 my $type = $1;
789 37         161 my $path = $2;
790 37   50     589 my $host = $3 || q{};
791 37 50       487 if ( $type eq 'inet' ) {
    50          
792 0         0 _warn(
793             join( ' ',
794             'metrics listening on inet',
795             "host=$host",
796             "port=$path",
797             "backlog=$listen_backlog",
798             )
799             );
800 0         0 push @ports, {
801             'host' => $host,
802             'port' => $path,
803             'ipv' => '*',
804             'proto' => 'tcp',
805             };
806             }
807             elsif ( $type eq 'unix' ) {
808 37         672 _warn(
809             join( ' ',
810             'metrics listening on unix',
811             "socket=$path",
812             "backlog=$listen_backlog",
813             )
814             );
815 37         786 push @ports, {
816             'port' => $path,
817             'proto' => 'unix',
818             };
819              
820 37 50       255 if ($umask) {
821 37         306 umask ( oct( $umask ) );
822 37         274 _warn( 'setting umask to ' . $umask );
823             }
824              
825             }
826             else {
827 0         0 die 'Invalid metrics connection';
828             }
829              
830 37 50       531 if ( defined( $config->{'metric_port'} ) ) {
831 0         0 _warn( 'metric_port ignored when metric_connection supplied' );
832             }
833              
834             }
835             elsif ( defined( $config->{'metric_port'} ) ) {
836 0   0     0 my $metric_host = $config->{ 'metric_host' } || '127.0.0.1';
837             push @ports, {
838             'host' => $metric_host,
839 0         0 'port' => $config->{'metric_port'},
840             'ipv' => '*',
841             'proto' => 'tcp',
842             };
843 0         0 _warn( 'Metrics available on ' . $metric_host . ':' . $config->{'metric_port'} );
844 0         0 _warn( 'metric_host/metric_port are depricated, please use metric_connection/metric_umask instead' );
845             }
846              
847 37         227 $srvargs{'port'} = \@ports;
848 37         594 $srvargs{'listen'} = $listen_backlog;
849 37         533 $srvargs{'leave_children_open_on_hup'} = 1;
850              
851 37 50 33     525 if ( $config->{'patch_net_server'} && scalar @ports == 1 ) {
852 0         0 my $error = 'Net::Server patches can not be applied when listening on a single port';
853 0         0 _warn $error;
854 0         0 die;
855             }
856              
857 37   50     1207 $srvargs{'max_dequeue'} = $config->{'max_dequeue'} // 5;
858 37   50     378 $srvargs{'check_for_dequeue'} = $config->{'check_for_dequeue'} // 60;
859              
860 37         573 _warn "==========";
861 37         342 _warn "Starting server";
862 37         1032 _warn "Running with perl $PERL_VERSION";
863 37         421 _warn "==========";
864              
865 37         217 my @start_times;
866 37         327 my $parent_pid = $PID;
867 37         223 while ( 1 ) {
868 37         193 unshift @start_times, time();
869              
870 37         116 eval {
871 37         5531 __PACKAGE__->run( %srvargs );
872             };
873 1         205 my $error = $@;
874 1 50       10 if ( $PID != $parent_pid ) {
875 1         6 _warn "Child exiting";
876 1         676 die;
877             }
878 0 0       0 $error = 'unknown error' if ! $error;
879 0         0 _warn "Server failed: $error";
880              
881             # We exited abnormally, try and clean up
882 0         0 eval{ __PACKAGE__->close_children };
  0         0  
883 0         0 eval{ __PACKAGE__->post_child_cleanup_hook };
  0         0  
884 0         0 eval{ __PACKAGE__->shutdown_sockets };
  0         0  
885 0         0 sleep 10;
886              
887 0 0       0 if ( scalar @start_times >= 4 ) {
888 0 0       0 if ( $start_times[3] > ( time() - 120 ) ) {
889 0         0 eval{ send_panic_email("Error: $error - Abandoning") };
  0         0  
890 0         0 _warn "Abandoning automatic restart: too many restarts in a short time";
891 0         0 last;
892             }
893             }
894              
895 0         0 eval{ send_panic_email("Error: $error - Attempting automatic restart") };
  0         0  
896 0         0 _warn "Attempting automatic restart";
897 0         0 sleep 10;
898             }
899 0         0 _warn "Server exiting abnormally";
900 0         0 die;
901             }
902              
903             ##### Protocol methods
904              
905              
906 1     1 1 5 sub fatal($self,$error) {
  1         10  
  1         4  
  1         1  
907 1         14 $self->logerror( "Child process $PID shutting down due to fatal error: $error" );
908 1         1233 die "$error\n";
909             }
910              
911              
912 0     0 1 0 sub fatal_global($self,$error) {
  0         0  
  0         0  
  0         0  
913 0         0 my $ppid = $self->{'server'}->{'ppid'};
914 0 0       0 if ( $ppid == $PID ) {
915 0         0 $self->logerror( "Global shut down due to fatal error: $error" );
916             }
917             else {
918 0         0 $self->logerror( "Child process $PID signalling global shut down due to fatal error: $error" );
919 0         0 kill 'TERM', $ppid;
920             }
921 0         0 die "$error\n";
922             }
923              
924              
925 47     47 1 339 sub setup_handlers($self) {
  47         509  
  47         381  
926 47         1006 $self->logdebug( 'setup objects' );
927 47         14411 my $handler = Mail::Milter::Authentication::Handler->new( $self );
928 47         829 $self->{'handler'}->{'_Handler'} = $handler;
929              
930 47         556 my $config = $self->{'config'};
931 47         703 foreach my $name ( $config->{'load_handlers'}->@* ) {
932 278         1579 $self->setup_handler( $name );
933             }
934 47         634 $self->sort_all_callbacks();
935             }
936              
937              
938 377     377 1 860 sub load_handler($self,$name) {
  377         845  
  377         1171  
  377         906  
939             ## TODO error handling here
940 377         1771 $self->logdebug( "Load Handler $name" );
941              
942 377         28958 my $package = "Mail::Milter::Authentication::Handler::$name";
943 377 100       1682 if ( ! is_loaded ( $package ) ) {
944 348         9309 $self->logdebug( "Load Handler Module $name" );
945 348         17715 eval { load $package; };
  348         1481  
946 348 50       6656 if ( my $error = $@ ) {
947 0         0 $self->fatal_global('Could not load handler ' . $name . ' : ' . $error);
948             }
949             }
950             }
951              
952              
953 278     278 1 794 sub setup_handler($self,$name) {
  278         727  
  278         1400  
  278         748  
954             ## TODO error handling here
955 278         1807 $self->logdebug( "Instantiate Handler $name" );
956              
957 278         26429 my $package = "Mail::Milter::Authentication::Handler::$name";
958 278         5184 my $object = $package->new( $self );
959 278         1326 $self->{'handler'}->{$name} = $object;
960              
961 278         1537 foreach my $callback ( qw { metrics setup connect helo envfrom envrcpt header eoh body eom addheader abort close dequeue } ) {
962 3892 100       46883 if ( $object->can( $callback . '_callback' ) ) {
963 1295         4749 $self->register_callback( $name, $callback );
964             }
965             }
966             }
967              
968              
969             sub destroy_handler {
970             # Unused!
971 233     233 1 573 my ( $self, $name ) = @_;
972             # Remove some back references
973 233         999 delete $self->{'handler'}->{$name}->{'thischild'};
974             # Remove reference to handler
975 233         758 delete $self->{'handler'}->{$name};
976             }
977              
978              
979 1295     1295 1 2383 sub register_callback($self,$name,$callback) {
  1295         2461  
  1295         2364  
  1295         2916  
  1295         2177  
980 1295         5456 $self->logdebug( "Register Callback $name:$callback" );
981 1295 100       146564 if ( ! exists $self->{'callbacks'}->{$callback} ) {
982 479         4406 $self->{'callbacks'}->{$callback} = [];
983             }
984 1295         2787 push @{ $self->{'callbacks'}->{$callback} }, $name;
  1295         5283  
985             }
986              
987              
988 47     47 1 207 sub sort_all_callbacks($self) {
  47         283  
  47         304  
989 47         357 foreach my $callback ( qw { metrics setup connect helo envfrom envrcpt header eoh body eom addheader abort close dequeue } ) {
990 658         2042 $self->sort_callbacks( $callback );
991             }
992             }
993              
994              
995 658     658 1 1235 sub sort_callbacks($self,$callback) {
  658         1168  
  658         3571  
  658         1247  
996 658 100       1973 if ( ! exists $self->{'callbacks'}->{$callback} ) {
997 179         1017 $self->{'callbacks'}->{$callback} = [];
998             }
999              
1000 658 50       1832 if ( ! exists $self->{'callbacks_list'}->{$callback} ) {
1001 658         6113 $self->{'callbacks_list'}->{$callback} = [];
1002             }
1003             else {
1004 0         0 return $self->{'callbacks_list'}->{$callback};
1005             }
1006              
1007 658         1500 my $callbacks_ref = $self->{'callbacks'}->{$callback};
1008              
1009 658         1120 my $added = {};
1010 658         1351 my @order;
1011              
1012 658         1099 my @todo = sort @{$callbacks_ref};
  658         2291  
1013 658         1277 my $todo_count = scalar @todo;
1014              
1015             # Process requirements
1016 658         1419 my $requirements = {};
1017 658         1651 foreach my $item ( @todo ) {
1018 1295         3270 $requirements->{ $item } = [];
1019 1295         2415 my $handler = $self->{'handler'}->{ $item };
1020 1295         2631 my $requires_method = $callback . '_requires';
1021 1295 100       11170 if ( $handler->can( $requires_method ) ) {
1022 82         1563 my $requires = $handler->$requires_method;
1023 82         401 foreach my $require ( $requires->@* ) {
1024 103         270 push @{ $requirements->{ $item } }, $require;
  103         547  
1025             }
1026             }
1027             }
1028 658         1459 foreach my $item ( @todo ) {
1029 1295         5217 my $handler = $self->{'handler'}->{ $item };
1030 1295         2437 my $requires_method = $callback . '_required_before';
1031 1295 50       10048 if ( $handler->can( $requires_method ) ) {
1032 0         0 my $requires = $handler->$requires_method;
1033 0         0 foreach my $require ( $requires->@* ) {
1034 0         0 push @{ $requirements->{ $require } }, $item;
  0         0  
1035             }
1036             }
1037             }
1038              
1039 658         1700 while ( $todo_count ) {
1040 531         966 my @defer;
1041 531         974 foreach my $item ( @todo ) {
1042 1349         2122 my $requires_met = 1;
1043 1349         2062 my $requires = $requirements->{ $item };
1044 1349         2160 foreach my $require ( $requires->@* ) {
1045 178 100       703 if ( ! exists $added->{$require} ) {
1046 74         321 $requires_met = 0;
1047             }
1048             }
1049 1349 100       2516 if ( $requires_met == 1 ) {
1050 1295         2870 push @order, $item;
1051 1295         2943 $added->{$item} = 1;
1052             }
1053             else {
1054 54         211 push @defer, $item;
1055             }
1056             }
1057              
1058 531         3448 my $defer_count = scalar @defer;
1059 531 50       1585 if ( $defer_count == $todo_count ) {
1060 0         0 $self->fatal_global('Could not build order list');
1061             }
1062 531         1105 $todo_count = $defer_count;
1063 531         1495 @todo = @defer;
1064             }
1065              
1066 658         2842 $self->{'callbacks_list'}->{$callback} = \@order;
1067             }
1068              
1069              
1070 25     25 1 91 sub destroy_objects($self) {
  25         100  
  25         73  
1071 25         313 $self->logdebug ( 'destroy objects' );
1072 25         90 my $handler = $self->{'handler'}->{'_Handler'};
1073 25 50       153 if ( $handler ) {
1074 25         220 $handler->destroy_all_objects();
1075 25         452 my $config = $self->{'config'};
1076 25         275 foreach my $name ( $config->{'load_handlers'}->@* ) {
1077 233         600 $self->destroy_handler( $name );
1078             }
1079 25         121 delete $self->{'handler'}->{'_Handler'}->{'config'};
1080 25         92 delete $self->{'handler'}->{'_Handler'}->{'thischild'};
1081 25         1001 delete $self->{'handler'}->{'_Handler'};
1082             }
1083             }
1084              
1085              
1086              
1087              
1088             ## Logging
1089              
1090              
1091 8087     8087 1 13160 sub get_queue_id($self) {
  8087         14143  
  8087         13440  
1092 8087         12755 my $queue_id;
1093              
1094 8087 100       28273 if ( exists ( $self->{'smtp'} ) ) {
    100          
1095 538 50       1831 if ( $self->{'smtp'}->{'queue_id'} ) {
1096 538         1218 $queue_id = $self->{'smtp'}->{'queue_id'};
1097             }
1098             }
1099             elsif ( exists ( $self->{'handler'}->{'_Handler'} ) ) {
1100 4369         18071 $queue_id = $self->{'handler'}->{'_Handler'}->get_symbol('i');
1101             }
1102              
1103 8087         25488 return $queue_id;
1104             }
1105              
1106              
1107 0     0 1 0 sub enable_extra_debugging($self) {
  0         0  
  0         0  
1108 0   0     0 my $config = $self->{'config'} || get_config();
1109 0         0 $config->{'logtoerr'} = 1;
1110 0         0 $config->{'debug'} = 1;
1111 0         0 $self->{'extra_debugging'} = 1;
1112 0         0 $self->logerror( 'Extra debugging enabled. Child will exit on close.' );
1113             # We don't want to persist this, so force an exit on close state.
1114 0         0 $self->{'handler'}->{'_Handler'}->{'exit_on_close'} = 1;
1115             }
1116              
1117              
1118 2598     2598 1 4574 sub extra_debugging($self,$line) {
  2598         4015  
  2598         4235  
  2598         3770  
1119 2598 50       8494 if ( $self->{'extra_debugging'} ) {
1120 0         0 $self->logerror( $line );
1121             }
1122             }
1123              
1124              
1125 1     1 1 3 sub logerror($self,$line) {
  1         3  
  1         3  
  1         4  
1126 1   33     7 my $config = $self->{'config'} || get_config();
1127 1 50       9 if ( my $queue_id = $self->get_queue_id() ) {
1128 0         0 $line = $queue_id . ': ' . $line;
1129             }
1130 1 50       11 _warn( $line ) if $config->{'logtoerr'};
1131 1         7 logger()->log( { 'level' => 'error' }, $line );
1132             }
1133              
1134              
1135 127     127 1 893 sub loginfo($self,$line) {
  127         945  
  127         3462  
  127         487  
1136 127   33     904 my $config = $self->{'config'} || get_config();
1137 127 100       1460 if ( my $queue_id = $self->get_queue_id() ) {
1138 9         66 $line = $queue_id . ': ' . $line;
1139             }
1140 127 50       3607 _warn( $line ) if $config->{'logtoerr'};
1141 127         1318 logger()->log( { 'level' => 'info' }, $line );
1142             }
1143              
1144              
1145 7959     7959 1 14820 sub logdebug($self,$line) {
  7959         14247  
  7959         14255  
  7959         12753  
1146 7959   66     29420 my $config = $self->{'config'} || get_config();
1147 7959 100       20751 if ( my $queue_id = $self->get_queue_id() ) {
1148 1140         3195 $line = $queue_id . ': ' . $line;
1149             }
1150 7959 100       26151 if ( $config->{'debug'} ) {
1151 384 50       1848 _warn( $line ) if $config->{'logtoerr'};
1152 384         1786 logger()->log( { 'level' => 'debug' }, $line );
1153             }
1154             }
1155              
1156             1;
1157              
1158             __END__
1159              
1160             =pod
1161              
1162             =encoding UTF-8
1163              
1164             =head1 NAME
1165              
1166             Mail::Milter::Authentication - A Perl Mail Authentication Milter
1167              
1168             =head1 VERSION
1169              
1170             version 3.20230911
1171              
1172             =head1 SYNOPSIS
1173              
1174             Subclass of Net::Server::PreFork for bringing up the main server process for authentication_milter.
1175              
1176             This class handles the server aspects of Authentication Milter.
1177              
1178             For individual Protocol handling please see the Mail::Milter::Authentication::Protocol::* classes
1179              
1180             For request handling please see Mail::Milter::Authentication::Handler
1181              
1182             Please see Net::Server docs for more detail of the server code.
1183              
1184             Please see the output of 'authentication_milter --help' for usage help.
1185              
1186             =head1 DESCRIPTION
1187              
1188             A Perl Implementation of email authentication standards rolled up into a single easy to use milter.
1189              
1190             =head1 METHODS
1191              
1192             =head2 preload_modules( $from, $matching )
1193              
1194             Preload (pre-fork) lazy loading modules.
1195              
1196             Takes a Package Name and a Base module, and loads all modules which match.
1197              
1198             =head2 I<write_to_log_hook()>
1199              
1200             Hook which runs to write logs
1201              
1202             =head2 I<idle_loop_hook()>
1203              
1204             Hook which runs in the parent periodically.
1205              
1206             =head2 I<pre_loop_hook()>
1207              
1208             Hook which runs in the parent before looping.
1209              
1210             =head2 I<run_n_children_hook()>
1211              
1212             Hook which runs in parent before it forks children.
1213              
1214             =head2 I<child_init_hook()>
1215              
1216             Hook which runs after forking, sets up per process items.
1217              
1218             =head2 I<child_finish_hook()>
1219              
1220             Hook which runs when the child is about to finish.
1221              
1222             =head2 I<pre_server_close_hook()>
1223              
1224             Hook which runs before the server closes.
1225              
1226             =head2 I<dequeue()>
1227              
1228             Call the dequeue handlers
1229              
1230             =head2 I<get_client_proto()>
1231              
1232             Get the protocol of the connecting client.
1233              
1234             =head2 I<get_client_port()>
1235              
1236             Get the port of the connecting client.
1237              
1238             =head2 I<get_client_host()>
1239              
1240             Get the host of the connecting client.
1241              
1242             =head2 I<get_client_path()>
1243              
1244             Get the path of the connecting client.
1245              
1246             =head2 I<get_client_details()>
1247              
1248             Get the details of the connecting client.
1249              
1250             =head2 I<process_request()>
1251              
1252             Hook which runs for each request, passes control to metrics handler or process_main as appropriate.
1253              
1254             =head2 I<process_main()>
1255              
1256             Method which runs for each request, sets up per request items and processes the request.
1257              
1258             =head2 I<send_exception_email()>
1259              
1260             Send an email to the administrator with details of a problem.
1261              
1262             =head2 I<fatal($error)>
1263              
1264             Log a fatal error and die in child
1265              
1266             =head2 I<fatal_global($error)>
1267              
1268             Log a fatal error and die in child and parent
1269              
1270             =head2 I<setup_handlers()>
1271              
1272             Setup the Handler objects.
1273              
1274             =head2 I<load_handler( $name )>
1275              
1276             Load the $name Handler module
1277              
1278             =head2 I<setup_handler( $name )>
1279              
1280             Setup the $name Handler object
1281              
1282             =head2 I<destroy_handler( $name )>
1283              
1284             Remove the $name Handler
1285              
1286             =head2 I<register_callback( $name, $callback )>
1287              
1288             Register the specified callback
1289              
1290             =head2 I<sort_all_callbacks()>
1291              
1292             Sort the callbacks into the order in which they must be called
1293              
1294             =head2 I<sort_callbacks( $callback )>
1295              
1296             Sort the callbacks for the $callback callback into the right order
1297              
1298             =head2 I<destroy_objects()>
1299              
1300             Remove references to all objects
1301              
1302             =head2 I<get_queue_id()>
1303              
1304             Return the queue ID (for logging) if possible.
1305              
1306             =head2 I<enable_extra_debugging()>
1307              
1308             Turn on extra debugging mode, will cause child to exit on close.
1309              
1310             =head2 I<extra_debugging( $line )>
1311              
1312             Cause $line to be written to log if extra debugging mode is enabled.
1313              
1314             =head2 I<logerror( $line )>
1315              
1316             Log to the error log.
1317              
1318             =head2 I<loginfo( $line )>
1319              
1320             Log to the info log.
1321              
1322             =head2 I<logdebug( $line )>
1323              
1324             Log to the debug log.
1325              
1326             =head1 FUNCTIONS
1327              
1328             =head2 I<get_installed_handlers()>
1329              
1330             Return an array ref of installed handler modules.
1331              
1332             =head2 I<send_panic_email()>
1333              
1334             Send an email to the administrator with details of a problem.
1335              
1336             Called from the parent process if the server exits.
1337              
1338             =head2 I<get_valid_pid($pid_file)>
1339              
1340             Given a pid file, check for a valid process ID and return if valid.
1341              
1342             =head2 I<find_process()>
1343              
1344             Search the process table for an authentication_milter parent process
1345              
1346             =head2 I<control($command)>
1347              
1348             Run a daemon command. Command can be one of start/restart/stop/status.
1349              
1350             =head2 I<start($hashref)>
1351              
1352             Start the server. This method does not return.
1353              
1354             $hashref = {
1355             'pid_file' => 'The pid file to use', #
1356             'daemon' => 1/0, # Daemonize process?
1357             }
1358              
1359             =head1 AUTHOR
1360              
1361             Marc Bradshaw <marc@marcbradshaw.net>
1362              
1363             =head1 COPYRIGHT AND LICENSE
1364              
1365             This software is copyright (c) 2020 by Marc Bradshaw.
1366              
1367             This is free software; you can redistribute it and/or modify it under
1368             the same terms as the Perl 5 programming language system itself.
1369              
1370             =cut