File Coverage

blib/lib/Mail/Milter/Authentication.pm
Criterion Covered Total %
statement 514 767 67.0
branch 115 234 49.1
condition 30 130 23.0
subroutine 51 60 85.0
pod 39 40 97.5
total 749 1231 60.8


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