
mmartinec at apache
Nov 18, 2009, 4:51 PM
Post #1 of 1
(250 views)
Permalink
|
|
svn commit: r882020 - in /spamassassin/trunk/lib/Mail: ./ SpamAssassin/ SpamAssassin/Plugin/
|
|
Author: mmartinec Date: Thu Nov 19 00:51:09 2009 New Revision: 882020 URL: http://svn.apache.org/viewvc?rev=882020&view=rev Log: Bug 6238: introducing the 'time_limit' configuration option, with associated code changes in various places Modified: spamassassin/trunk/lib/Mail/SpamAssassin.pm spamassassin/trunk/lib/Mail/SpamAssassin/AsyncLoop.pm spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgLearner.pm spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Check.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Razor2.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm spamassassin/trunk/lib/Mail/SpamAssassin/Timeout.pm Modified: spamassassin/trunk/lib/Mail/SpamAssassin.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin.pm Thu Nov 19 00:51:09 2009 @@ -485,8 +485,9 @@ sub parse { my($self, $message, $parsenow, $suppl_attrib) = @_; - $self->init(1); + my $start_time = time; + $self->init(1); my $timer = $self->time_method("parse"); my $msg = Mail::SpamAssassin::Message->new({ @@ -494,6 +495,15 @@ normalize=>$self->{conf}->{normalize_charset}, suppl_attrib=>$suppl_attrib }); + if (ref $suppl_attrib && exists $suppl_attrib->{master_deadline}) { + $msg->{master_deadline} = $suppl_attrib->{master_deadline}; # may be undef + } elsif ($self->{conf}->{time_limit}) { # defined and nonzero + $msg->{master_deadline} = $start_time + $self->{conf}->{time_limit}; + } + if (defined $msg->{master_deadline}) { + dbg("config: time limit %.1f s", $msg->{master_deadline} - $start_time); + } + # bug 5069: The goal here is to get rendering plugins to do things # like OCR, convert doc and pdf to text, etc, though it could be anything # that wants to process the message after it's been parsed. @@ -524,10 +534,10 @@ my ($self, $mail_obj) = @_; $self->init(1); - my $msg = Mail::SpamAssassin::PerMsgStatus->new($self, $mail_obj); - $msg->check(); + my $pms = Mail::SpamAssassin::PerMsgStatus->new($self, $mail_obj); + $pms->check(); dbg("timing: " . $self->timer_report()) if $self->{timer_enabled}; - $msg; + $pms; } =item $status = $f->check_message_text ($mailtext) @@ -1350,7 +1360,7 @@ "Message-Id: <".time."\@spamassassin_spamd_init>\n", "\n", "I need to make this message body somewhat long so TextCat preloads\n"x20); - my $mail = $self->parse(\@testmsg, 1); + my $mail = $self->parse(\@testmsg, 1, { master_deadline => undef }); my $status = Mail::SpamAssassin::PerMsgStatus->new($self, $mail, { disable_auto_learning => 1 } ); Modified: spamassassin/trunk/lib/Mail/SpamAssassin/AsyncLoop.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/AsyncLoop.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/AsyncLoop.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/AsyncLoop.pm Thu Nov 19 00:51:09 2009 @@ -168,7 +168,7 @@ =cut sub start_lookup { - my ($self, $ent) = @_; + my ($self, $ent, $master_deadline) = @_; die "oops, no id" unless $ent->{id} ne ''; die "oops, no key" unless $ent->{key} ne ''; @@ -210,8 +210,17 @@ $t_end = $settings->{rbl_timeout_min} if $settings && !defined $t_end; $t_end = 0.2 * $t_init if !defined $t_end; $t_end = 0 if $t_end < 0; # just in case - $t_init = $t_end if $t_init < $t_end; + + my $clipped_by_master_deadline = 0; + if (defined $master_deadline) { + my $time_avail = $master_deadline - time; + $time_avail = 0.5 if $time_avail < 0.5; # give some slack + if ($t_init > $time_avail) { + $t_init = $time_avail; $clipped_by_master_deadline = 1; + $t_end = $time_avail if $t_end > $time_avail; + } + } $ent->{timeout_initial} = $t_init; $ent->{timeout_min} = $t_end; @@ -224,8 +233,9 @@ $self->{total_queries_started}++; $self->{pending_lookups}->{$key} = $ent; - dbg("async: starting: %s (timeout %.1fs, min %.1fs)", - $ent->{display_id}, $ent->{timeout_initial}, $ent->{timeout_min}); + dbg("async: starting: %s (timeout %.1fs, min %.1fs)%s", + $ent->{display_id}, $ent->{timeout_initial}, $ent->{timeout_min}, + !$clipped_by_master_deadline ? '' : ', capped by time limit'); $ent; } Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm Thu Nov 19 00:51:09 2009 @@ -1573,6 +1573,61 @@ =over 4 +=item time_limit n (default: 0, i.e. unlimited) + +Specifies a limit on elapsed time in seconds that SpamAssassin is allowed +to spend before providing a result. The value may be fractional and must not +be negative, zero is interpreted as unlimited and is a default. + +This is a best-effort advisory setting, processing will not be abruptly +aborted at an arbitrary point in processing when the time limit is exceeded, +but only on reaching one of locations in the program flow equipped with a +time test. Currently equipped with the test are the main checking loop, +asynchronous DNS lookups, plugins which are calling external programs. +Rule evaluation is guarded by starting a timer (alarm) on each set of +compiled rules. + +When a message is passed to Mail::SpamAssassin::parse, a deadline time +is established as a sum of current time and the C<time_limit> setting. +This deadline may be overruled by a caller through option 'master_deadline' +in $suppl_attrib on a call to parse(), possibly providing a more accurate +deadline taking into account past and expected future processing of a +message in a mail filtering setup. + +When a time limit is exceeded, most of the remaining tests will be skipped, +as well as auto-learning. Whatever tests fired so far will determine the +final score. The behaviour is similar to short-circuiting with attribute 'on', +as implemented by a Shortcircuit plugin. A synthetic hit on a rule named +TIME_LIMIT_EXCEEDED with a near-zero score is generated, so that the report +will reflect the event. + +The C<time_limit> option is a useful protection against excessive processing +time on certain degenerate or unusually long or complex mail messages, as well +as against some DoS attacks. It is also needed in time-critical pre-queue +filtering setups (e.g. milter, proxy, integration with MTA), where message +processing must finish before a SMTP client times out. RFC 5321 prescribes +in section 4.5.3.2.6 the 'DATA Termination' time limit of 10 minutes, +although it is not unusual to see some SMTP clients abort sooner on waiting +for a response. A sensible C<time_limit> for a pre-queue filtering setup is +maybe 50 seconds, assuming that clients are willing to wait at least a minute. + +=cut + + push (@cmds, { + setting => 'time_limit', + default => 0, + type => $CONF_TYPE_NUMERIC, + code => sub { + my ($self, $key, $value, $line) = @_; + if ($value !~ /^\d+(?:\.\d*)?$/) { return $INVALID_VALUE } + $value = 0+$value; + if ($value < 0) { return $INVALID_VALUE } + $self->{time_limit} = $value; + } + }); + +=over 4 + =item lock_method type Select the file-locking method used to protect database files on-disk. By @@ -2549,7 +2604,7 @@ All DNS queries are made at the beginning of a check and we try to read the results at the end. This value specifies the maximum period of time -(in seconds) to wait for an DNS query. If most of the DNS queries have +(in seconds) to wait for a DNS query. If most of the DNS queries have succeeded for a particular message, then SpamAssassin will not wait for the full period to avoid wasting time on unresponsive server(s), but will shrink the timeout according to a percentage of queries already completed. Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm Thu Nov 19 00:51:09 2009 @@ -124,7 +124,8 @@ }); $ent->{id} = $id; # tie up the loose end - $existing = $self->{async}->start_lookup($ent); + $existing = + $self->{async}->start_lookup($ent, $self->{master_deadline}); } # always add set @@ -172,7 +173,7 @@ }); $ent->{id} = $id; # tie up the loose end - $self->{async}->start_lookup($ent); + $self->{async}->start_lookup($ent, $self->{master_deadline}); } ########################################################################### Modified: spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgLearner.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgLearner.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgLearner.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgLearner.pm Thu Nov 19 00:51:09 2009 @@ -73,6 +73,7 @@ 'main' => $main, 'msg' => $msg, 'learned' => 0, + 'master_deadline' => $msg->{master_deadline}, }; $self->{conf} = $self->{main}->{conf}; Modified: spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm Thu Nov 19 00:51:09 2009 @@ -53,6 +53,8 @@ use warnings; use re 'taint'; +use Time::HiRes qw(time); + use Mail::SpamAssassin::Constants qw(:sa); use Mail::SpamAssassin::AsyncLoop; use Mail::SpamAssassin::Conf; @@ -92,7 +94,9 @@ 'disable_auto_learning' => 0, 'auto_learn_status' => undef, 'conf' => $main->{conf}, - 'async' => Mail::SpamAssassin::AsyncLoop->new($main) + 'async' => Mail::SpamAssassin::AsyncLoop->new($main), + 'master_deadline' => $msg->{master_deadline}, # typically just inherited + 'deadline_exceeded' => 0, # time limit exceeded, skipping further tests }; #$self->{main}->{use_rule_subs} = 1; @@ -2200,7 +2204,6 @@ to be appended to an existing list of flags in $self->{conf}->{tflags}, such as: "nice noautolearn multiple". No syntax checks are performed. - =item description => $string Optional: a custom rule description string. This is used in the Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm Thu Nov 19 00:51:09 2009 @@ -225,7 +225,7 @@ key=>$key, id=>$id, type=>'TXT', zone => $zone, # serves to fetch other per-zone settings }; - $scanner->{async}->start_lookup($ent); + $scanner->{async}->start_lookup($ent, $scanner->{master_deadline}); dbg("asn: launched DNS TXT query for %s.%s in background", $reversed_ip_quad, $entry->{zone}); Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Check.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Check.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Check.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Check.pm Thu Nov 19 00:51:09 2009 @@ -18,9 +18,12 @@ use warnings; use re 'taint'; +use Time::HiRes qw(time); + use Mail::SpamAssassin::Plugin; use Mail::SpamAssassin::Logger; use Mail::SpamAssassin::Util qw(untaint_var); +use Mail::SpamAssassin::Timeout; use Mail::SpamAssassin::Constants qw(:sa); use vars qw(@ISA @TEMPORARY_METHODS); @@ -49,7 +52,7 @@ my $pms = $args->{permsgstatus}; my $suppl_attrib = $pms->{msg}->{suppl_attrib}; - if (defined $suppl_attrib && ref $suppl_attrib->{rule_hits}) { + if (ref $suppl_attrib && ref $suppl_attrib->{rule_hits}) { my @caller_rule_hits = @{$suppl_attrib->{rule_hits}}; dbg("check: adding caller rule hits, %d rules", scalar(@caller_rule_hits)); for my $caller_rule_hit (@caller_rule_hits) { @@ -79,6 +82,7 @@ my $decoded = $pms->get_decoded_stripped_body_text_array(); my $bodytext = $pms->get_decoded_body_text_array(); my $fulltext = $pms->{msg}->get_pristine(); + my $master_deadline = $pms->{master_deadline}; my @uris = $pms->get_uri_list(); @@ -87,11 +91,19 @@ # happen in Conf.pm when we switch a rule from one priority to another next unless ($pms->{conf}->{priorities}->{$priority} > 0); - my $timer = $self->{main}->time_method("tests_pri_".$priority); - - # if shortcircuiting is hit, we skip all other priorities... - last if $self->{main}->call_plugins("have_shortcircuited", { permsgstatus => $pms }); + if ($pms->{deadline_exceeded}) { + last; + } elsif ($master_deadline && time > $master_deadline) { + info("check: exceeded time limit, skipping further tests"); + $pms->{deadline_exceeded} = 1; + last; + } elsif ($self->{main}->call_plugins("have_shortcircuited", + { permsgstatus => $pms })) { + # if shortcircuiting is hit, we skip all other priorities... + last; + } + my $timer = $self->{main}->time_method("tests_pri_".$priority); dbg("check: running tests for priority: $priority"); # only harvest the dnsbl queries once priority HARVEST_DNSBL_PRIORITY @@ -122,33 +134,49 @@ # do head tests $self->do_head_tests($pms, $priority); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; + $self->do_head_eval_tests($pms, $priority); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; $self->do_body_tests($pms, $priority, $decoded); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; + $self->do_uri_tests($pms, $priority, @uris); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; + $self->do_body_eval_tests($pms, $priority, $decoded); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; $self->do_rawbody_tests($pms, $priority, $bodytext); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; + $self->do_rawbody_eval_tests($pms, $priority, $bodytext); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; $self->do_full_tests($pms, $priority, \$fulltext); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; + $self->do_full_eval_tests($pms, $priority, \$fulltext); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; $self->do_meta_tests($pms, $priority); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; # we may need to call this more often than once through the loop, but # it needs to be done at least once, either at the beginning or the end. $self->{main}->call_plugins ("check_tick", { permsgstatus => $pms }); $pms->harvest_completed_queries(); + last if $pms->{deadline_exceeded}; } # sanity check, it is possible that no rules >= HARVEST_DNSBL_PRIORITY ran so the harvest @@ -167,15 +195,27 @@ $pms->{resolver}->finish_socket() if $pms->{resolver}; } + if ($pms->{deadline_exceeded}) { + $pms->got_hit('TIME_LIMIT_EXCEEDED', '', score => 0.001, + description => 'Exceeded time limit / deadline'); + } + # finished running rules delete $pms->{current_rule_name}; undef $decoded; undef $bodytext; undef $fulltext; - # auto-learning - $pms->learn(); - $self->{main}->call_plugins ("check_post_learn", { permsgstatus => $pms }); + if ($pms->{deadline_exceeded}) { + # dbg("check: exceeded time limit, skipping auto-learning"); + } elsif ($master_deadline && time > $master_deadline) { + info("check: exceeded time limit, skipping auto-learning"); + $pms->{deadline_exceeded} = 1; + } else { + # auto-learning + $pms->learn(); + $self->{main}->call_plugins ("check_post_learn", { permsgstatus => $pms }); + } # track user_rules recompilations; each scanned message is 1 tick on this counter if ($self->{done_user_rules}) { @@ -239,8 +279,17 @@ sub run_generic_tests { my ($self, $pms, $priority, %opts) = @_; - return if $self->{main}->call_plugins("have_shortcircuited", - { permsgstatus => $pms }); + my $master_deadline = $pms->{master_deadline}; + if ($pms->{deadline_exceeded}) { + return; + } elsif ($master_deadline && time > $master_deadline) { + info("check: (run_generic) exceeded time limit, skipping further tests"); + $pms->{deadline_exceeded} = 1; + return; + } elsif ($self->{main}->call_plugins("have_shortcircuited", + { permsgstatus => $pms })) { + return; + } my $ruletype = $opts{type}; dbg("rules: running $ruletype tests; score so far=".$pms->{score}); @@ -257,11 +306,17 @@ my $methodname = $package_name."::_".$ruletype."_tests_".$clean_priority; if (defined &{$methodname} && !$doing_user_rules) { - no strict "refs"; run_compiled_method: # dbg("rules: run_generic_tests - calling %s", $methodname); - $methodname->($pms, @{$opts{args}}); - use strict "refs"; + my $t = Mail::SpamAssassin::Timeout->new({ deadline => $master_deadline }); + my $err = $t->run(sub { + no strict "refs"; + $methodname->($pms, @{$opts{args}}); + }); + if ($t->timed_out() && $master_deadline && time > $master_deadline) { + info("check: exceeded time limit in $methodname, skipping further tests"); + $pms->{deadline_exceeded} = 1; + } return; } @@ -1030,8 +1085,17 @@ sub run_eval_tests { my ($self, $pms, $testtype, $evalhash, $prepend2desc, $priority, @extraevalargs) = @_; - return if $self->{main}->call_plugins("have_shortcircuited", - { permsgstatus => $pms }); + my $master_deadline = $pms->{master_deadline}; + if ($pms->{deadline_exceeded}) { + return; + } elsif ($master_deadline && time > $master_deadline) { + info("check: (run_eval) exceeded time limit, skipping further tests"); + $pms->{deadline_exceeded} = 1; + return; + } elsif ($self->{main}->call_plugins("have_shortcircuited", + { permsgstatus => $pms })) { + return; + } my $conf = $pms->{conf}; my $doing_user_rules = $conf->{want_rebuild_for_type}->{$testtype}; @@ -1053,10 +1117,17 @@ if (defined &{"${package_name}::${methodname}"} && !$doing_user_rules) { + my $method = "${package_name}::${methodname}"; # dbg("rules: run_eval_tests - calling %s", $methodname); - no strict "refs"; - &{"${package_name}::${methodname}"}($pms,@extraevalargs); - use strict "refs"; + my $t = Mail::SpamAssassin::Timeout->new({ deadline => $master_deadline }); + my $err = $t->run(sub { + no strict "refs"; + &{$method}($pms,@extraevalargs); + }); + if ($t->timed_out() && $master_deadline && time > $master_deadline) { + info("check: exceeded time limit in $method, skipping further tests"); + $pms->{deadline_exceeded} = 1; + } return; } @@ -1207,9 +1278,15 @@ my $method = "${package_name}::${methodname}"; push (@TEMPORARY_METHODS, $methodname); # dbg("rules: run_eval_tests - calling %s", $methodname); - no strict "refs"; - &{$method}($pms,@extraevalargs); - use strict "refs"; + my $t = Mail::SpamAssassin::Timeout->new({ deadline => $master_deadline }); + my $err = $t->run(sub { + no strict "refs"; + &{$method}($pms,@extraevalargs); + }); + if ($t->timed_out() && $master_deadline && time > $master_deadline) { + info("check: exceeded time limit in $method, skipping further tests"); + $pms->{deadline_exceeded} = 1; + } } } Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm Thu Nov 19 00:51:09 2009 @@ -679,7 +679,8 @@ $permsgstatus->enter_helper_run_mode(); - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $permsgstatus->{master_deadline} }); my $err = $timer->run_and_catch(sub { local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; @@ -757,7 +758,8 @@ my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext); my $pid; - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $permsgstatus->{master_deadline} }); my $err = $timer->run_and_catch(sub { local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm Thu Nov 19 00:51:09 2009 @@ -719,7 +719,8 @@ }; my $timeout = $pms->{conf}->{dkim_timeout}; - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $pms->{master_deadline} }); my $err = $timer->run_and_catch(sub { dbg("dkim: performing public key lookup and signature verification"); @@ -909,7 +910,8 @@ my $practices; # author domain signing practices object my $timeout = $pms->{conf}->{dkim_timeout}; - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $pms->{master_deadline} }); my $err = $timer->run_and_catch(sub { eval { if (Mail::DKIM::AuthorDomainPolicy->UNIVERSAL::can("fetch")) { Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm Thu Nov 19 00:51:09 2009 @@ -267,7 +267,8 @@ $permsgstatus->enter_helper_run_mode(); - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $permsgstatus->{master_deadline} }); my $err = $timer->run_and_catch(sub { local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" }; Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Razor2.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Razor2.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Razor2.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Razor2.pm Thu Nov 19 00:51:09 2009 @@ -140,7 +140,7 @@ } sub razor2_access { - my ($self, $fulltext, $type) = @_; + my ($self, $fulltext, $type, $deadline) = @_; my $timeout = $self->{main}->{conf}->{razor_timeout}; my $return = 0; my @results; @@ -155,7 +155,8 @@ Mail::SpamAssassin::PerMsgStatus::enter_helper_run_mode($self); - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $deadline }); my $err = $timer->run_and_catch(sub { local ($^W) = 0; # argh, warnings in Razor @@ -347,7 +348,7 @@ return unless $self->{main}->{conf}->{use_razor2}; return if $options->{report}->{options}->{dont_report_to_razor}; - if ($self->razor2_access($options->{text}, 'report')) { + if ($self->razor2_access($options->{text}, 'report', undef)) { $options->{report}->{report_available} = 1; info('reporter: spam reported to Razor'); $options->{report}->{report_return} = 1; @@ -365,7 +366,7 @@ return unless $self->{main}->{conf}->{use_razor2}; return if $options->{revoke}->{options}->{dont_report_to_razor}; - if ($self->razor2_access($options->{text}, 'revoke')) { + if ($self->razor2_access($options->{text}, 'revoke', undef)) { $options->{revoke}->{revoke_available} = 1; dbg('reporter: spam revoked from Razor'); $options->{revoke}->{revoke_return} = 1; @@ -394,7 +395,8 @@ # do it this way to make it easier to get out the results later from the # netcache plugin - ($return, @results) = $self->razor2_access($full, 'check'); + ($return, @results) = + $self->razor2_access($full, 'check', $permsgstatus->{master_deadline}); $self->{main}->call_plugins ('process_razor_result', { results => \@results, permsgstatus => $permsgstatus } ); Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm Thu Nov 19 00:51:09 2009 @@ -542,7 +542,8 @@ my $timeout = $scanner->{conf}->{spf_timeout}; - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $scanner->{master_deadline} }); $err = $timer->run_and_catch(sub { my $query = $self->{spf_server}->process($request); @@ -579,7 +580,8 @@ my $timeout = $scanner->{conf}->{spf_timeout}; - my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $timer = Mail::SpamAssassin::Timeout->new( + { secs => $timeout, deadline => $scanner->{master_deadline} }); $err = $timer->run_and_catch(sub { ($result, $comment) = $query->result(); Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm Thu Nov 19 00:51:09 2009 @@ -924,7 +924,7 @@ } } }; - $scanner->{async}->start_lookup($ent); + $scanner->{async}->start_lookup($ent, $scanner->{master_deadline}); return $ent; } Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Timeout.pm URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Timeout.pm?rev=882020&r1=882019&r2=882020&view=diff ============================================================================== --- spamassassin/trunk/lib/Mail/SpamAssassin/Timeout.pm (original) +++ spamassassin/trunk/lib/Mail/SpamAssassin/Timeout.pm Thu Nov 19 00:51:09 2009 @@ -23,7 +23,7 @@ # non-timeout code... - my $t = Mail::SpamAssassin::Timeout->new({ secs => 5 }); + my $t = Mail::SpamAssassin::Timeout->new({ secs => 5, deadline => $when }); $t->run(sub { # code to run with a 5-second timeout... @@ -58,6 +58,8 @@ use bytes; use re 'taint'; +use Time::HiRes qw(time alarm); + use vars qw{ @ISA }; @@ -74,7 +76,14 @@ =item secs => $seconds -timeout, in seconds. Optional; if not specified, no timeouts will be applied. +time interval, in seconds. Optional; if neither C<secs> nor C<deadline> is +specified, no timeouts will be applied. + +=item deadline => $unix_timestamp + +Unix timestamp (time in seconds since epoch) when a timeout is reached. +Optional; if neither B<secs> nor B<deadline> is specified, no timeouts will +be applied. If both are specified, the shorter interval of the two prevails. =back @@ -96,7 +105,8 @@ Run a code reference within the currently-defined timeout. -The timeout is as defined by the B<secs> parameter to the constructor. +The timeout is as defined by the B<secs> and B<deadline> parameters +to the constructor. Returns whatever the subroutine returns, or C<undef> on timeout. If the timer times out, C<$t-<gt>timed_out()> will return C<1>. @@ -123,13 +133,22 @@ delete $self->{timed_out}; - if (!$self->{secs}) { # no timeout! just call the sub and return. + my $secs = $self->{secs}; + my $deadline = $self->{deadline}; + + if (defined $deadline) { + my $dt = $deadline - time; + $dt = 1 if $dt < 1; # give some slack + $secs = $dt if !defined $secs || $dt < $secs; + } + + if (!defined $secs) { # no timeout! just call the sub and return. return &$sub; } # assertion - if ($self->{secs} < 0) { - die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $self->{secs}"; + if ($secs < 0) { + die "Mail::SpamAssassin::Timeout: oops? neg value for 'secs': $secs"; } my $oldalarm = 0; @@ -146,7 +165,7 @@ local $SIG{ALRM} = sub { $timedout++; die "__alarm__ignore__\n" }; local $SIG{__DIE__}; # bug 4631 - $oldalarm = alarm($self->{secs}); + $oldalarm = alarm($secs); $ret = &$sub; @@ -187,11 +206,7 @@ $self->{timed_out} = 1; } - if ($and_catch) { - return; # undef - } else { - return $ret; - } + return $and_catch ? undef : $ret; } ########################################################################### @@ -219,7 +234,16 @@ sub reset { my ($self) = @_; - alarm($self->{secs}); + + my $secs = $self->{secs}; + my $deadline = $self->{deadline}; + + if (defined $deadline) { + my $dt = $deadline - time; + $dt = 1 if $dt < 1; # give some slack + $secs = $dt if !defined $secs || $dt < $secs; + } + alarm($secs) if defined $secs; } ###########################################################################
|