From owner-freebsd-ports-bugs@FreeBSD.ORG Fri Aug 10 18:00:12 2007 Return-Path: Delivered-To: freebsd-ports-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 152CA16A418 for ; Fri, 10 Aug 2007 18:00:12 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id ECAA213C461 for ; Fri, 10 Aug 2007 18:00:11 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.1/8.14.1) with ESMTP id l7AI0BsV026718 for ; Fri, 10 Aug 2007 18:00:11 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.1/8.14.1/Submit) id l7AI0Br7026717; Fri, 10 Aug 2007 18:00:11 GMT (envelope-from gnats) Date: Fri, 10 Aug 2007 18:00:11 GMT Message-Id: <200708101800.l7AI0Br7026717@freefall.freebsd.org> To: freebsd-ports-bugs@FreeBSD.org From: "Michael Scheidell" Cc: Subject: Re: ports/115369: [MAINTAINER UPDATE] mail/p5-Mail-SpamAssassin from 3.2.1 to 3.2.3 X-BeenThere: freebsd-ports-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Michael Scheidell List-Id: Ports bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 10 Aug 2007 18:00:12 -0000 The following reply was made to PR ports/115369; it has been noted by GNATS. From: "Michael Scheidell" To: , "Michael Scheidell" Cc: Subject: Re: ports/115369: [MAINTAINER UPDATE] mail/p5-Mail-SpamAssassin from 3.2.1 to 3.2.3 Date: Fri, 10 Aug 2007 13:52:19 -0400 This is a multi-part message in MIME format. ------=_NextPart_000_0002_01C7DB55.AB82D610 Content-Type: text/plain; format=flowed; charset="iso-8859-1"; reply-type=original Content-Transfer-Encoding: 7bit Last minuite bug found, bug in 3.2.2 and 3.2.3. attached patch replaces previous patch. added Time::HiRes as dependency mentioned in SA bug 5589. added this file: files/patch-dnsbug-multiple removed this file: Only in /var/tmp/p5-Mail-SpamAssassin/files: patch-spamc-Makefile.in (already fixed in 3.2.2) If you have applied 115369, please add PORTREVISION= 1 to Makefile, if not, no need. bug described here, and slows down ASN plugin by 3 seconds each email. http://issues.apache.org/SpamAssassin/show_bug.cgi?id=5589 ------=_NextPart_000_0002_01C7DB55.AB82D610 Content-Type: text/plain; format=flowed; name="sa323_1.txt"; reply-type=original Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="sa323_1.txt" diff -bBru /var/tmp/p5-Mail-SpamAssassin/Makefile ./Makefile --- /var/tmp/p5-Mail-SpamAssassin/Makefile Sun Jul 22 18:40:58 2007 +++ ./Makefile Fri Aug 10 13:47:04 2007 @@ -6,8 +6,7 @@ # =20 PORTNAME=3D Mail-SpamAssassin -PORTVERSION=3D 3.2.1 -PORTREVISION=3D 1 +PORTVERSION=3D 3.2.3 CATEGORIES=3D mail perl5 MASTER_SITES=3D ${MASTER_SITE_APACHE:S/$/:apache/} = ${MASTER_SITE_PERL_CPAN:S/$/:cpan/} MASTER_SITE_SUBDIR=3D spamassassin/source/:apache Mail/:cpan @@ -27,7 +26,8 @@ RUN_DEPENDS=3D ${BUILD_DEPENDS} \ ${SITE_PERL}/Bundle/LWP.pm:${PORTSDIR}/www/p5-libwww \ p5-Archive-Tar>=3D1.23:${PORTSDIR}/archivers/p5-Archive-Tar \ - = ${SITE_PERL}/mach/Encode/Detect.pm:${PORTSDIR}/converters/p5-Encode-Detec= t + = ${SITE_PERL}/mach/Encode/Detect.pm:${PORTSDIR}/converters/p5-Encode-Detec= t \ + p5-Time-HiRes>=3D0:${PORTSDIR}/devel/p5-Time-HiRes =20 PERL_CONFIGURE=3D yes USE_LDCONFIG=3D yes @@ -97,7 +97,7 @@ .if defined(WITH_DKIM) RUN_DEPENDS+=3D p5-Mail-DKIM>=3D.20:${PORTSDIR}/mail/p5-Mail-DKIM RUN_DEPENDS+=3D = ${SITE_PERL}/IO/Socket/SSL.pm:${PORTSDIR}/security/p5-IO-Socket-SSL -RUN_DEPENDS+=3D = p5-Mail-DomainKeys>=3D0.80:${PORTSDIR}/mail/p5-Mail-DomainKeys +RUN_DEPENDS+=3D = p5-Crypt-OpenSSL-RSA>=3D0.24:${PORTSDIR}/security/p5-Crypt-OpenSSL-RSA .endif =20 .if defined(WITH_SACOMPILE) @@ -212,7 +212,6 @@ .endif .if defined(WITH_DKIM) ${REINPLACE_CMD} -e '/DKIM/s/^#loadplugin/loadplugin/' = ${WRKSRC}/rules/v312.pre - ${REINPLACE_CMD} -e '/DomainKeys/s/^#loadplugin/loadplugin/' = ${WRKSRC}/rules/v310.pre .endif .if !defined(WITH_SPF_QUERY) ${REINPLACE_CMD} -e '/SPF/s/^loadplugin/#loadplugin/' = ${WRKSRC}/rules/init.pre @@ -266,10 +265,5 @@ =20 .endif @${SED} -e 's#PREFIX#${PREFIX}#' ${PKGMESSAGE} - -.if ${PERL_LEVEL} < 500800 -BUILD_DEPENDS+=3D = ${SITE_PERL}/${PERL_ARCH}/MIME/Base64.pm:${PORTSDIR}/converters/p5-MIME-B= ase64 \ - ${SITE_PERL}/${PERL_ARCH}/Storable.pm:${PORTSDIR}/devel/p5-Storable -.endif =20 .include diff -bBru /var/tmp/p5-Mail-SpamAssassin/distinfo ./distinfo --- /var/tmp/p5-Mail-SpamAssassin/distinfo Sat Jun 16 18:17:03 2007 +++ ./distinfo Thu Aug 9 19:28:55 2007 @@ -1,3 +1,3 @@ -MD5 (Mail-SpamAssassin-3.2.1.tar.gz) =3D = a7d51294c565999da01f212e5ad2a031 -SHA256 (Mail-SpamAssassin-3.2.1.tar.gz) =3D = e0925d9c490bb8f1e56c3b850b50b12b124536dfe581b23d3c25715b1ce9ebf7 -SIZE (Mail-SpamAssassin-3.2.1.tar.gz) =3D 1193561 +MD5 (Mail-SpamAssassin-3.2.3.tar.gz) =3D = 2e356b70b9458b44a828c19f6e816521 +SHA256 (Mail-SpamAssassin-3.2.3.tar.gz) =3D = 2bf7635555dea4912512a32c7c567094ef264770b86029c49f653e97352efad8 +SIZE (Mail-SpamAssassin-3.2.3.tar.gz) =3D 1202082 diff -bBru /var/tmp/p5-Mail-SpamAssassin/files/patch-ImageInfo.pm = ./files/patch-ImageInfo.pm --- /var/tmp/p5-Mail-SpamAssassin/files/patch-ImageInfo.pm Sat Jun 2 = 03:09:46 2007 +++ ./files/patch-ImageInfo.pm Sun Jul 29 19:47:26 2007 @@ -1,98 +1,17 @@ --- ImageInfo.pm.orig Tue May 1 09:54:09 2007 +++ lib/Mail/SpamAssassin/Plugin/ImageInfo.pm Thu May 3 16:08:29 2007 -@@ -1,10 +1,9 @@ - # <@LICENSE> --# Licensed to the Apache Software Foundation (ASF) under one or more --# contributor license agreements. See the NOTICE file distributed = with --# this work for additional information regarding copyright ownership. --# The ASF licenses this file to you under the Apache License, Version = 2.0 --# (the "License"); you may not use this file except in compliance with --# the License. You may obtain a copy of the License at: -+# Copyright 2004 Apache Software Foundation -+#=20 -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at - #=20 - # http://www.apache.org/licenses/LICENSE-2.0 - #=20 -@@ -16,6 +15,36 @@ - # - # - # ------------------------------------------------------- -+# ImageInfo Plugin for SpamAssassin -+# Version: 0.7 -+# Current Home: http://www.rulesemporium.com/plugins.htm#imageinfo -+# Created: 2006-08-02 -+# Modified: 2007-01-17 -+# By: Dallas Engelken -+# -+# Changes:=20 -+# 0.7 - added image_name_regex to allow pattern matching on the = image name -+# - added support for image/pjpeg content types (progressive = jpeg) -+# - updated imageinfo.cf with a few sample rules for using = image_name_regex() -+# 0.6 - fixed dems_ bug in image_size_range_ -+# 0.5 - added image_named and image_to_text_ratio -+# 0.4 - added image_size_exact and image_size_range -+# 0.3 - added jpeg support -+# 0.2 - optimized by theo -+# 0.1 - added gif/png support -+# -+# Files: -+# ImageInfo.pm (plugin) - = http://www.rulesemporium.com/plugins/ImageInfo.pm -+# imageinfo.cf (ruleset) - = http://www.rulesemporium.com/plugins/imageinfo.cf -+# =20 -+# Install: -+# 1) place ruleset in your local config dir -+# 2) place plugin in your plugins dir=20 -+# 3) add to init.pre (or v310.pre) the following line -+# loadplugin Mail::SpamAssassin::Plugin::ImageInfo -+# or if not in plugin dir.. -+# loadplugin Mail::SpamAssassin::Plugin::ImageInfo = /path/to/plugin -+# 4) restart spamd (if necessary) - # - # Usage: - # image_count() -@@ -27,7 +56,7 @@ - # max: optional, if specified, message must not=20 - # contain more than this number of images +@@ -49,6 +49,10 @@ + # body LARGE_IMAGE_AREA eval:pixel_coverage('all',150000) + # body SMALL_GIF_AREA eval:pixel_coverage('gif',1,40000) # --# examples -+# image_count() examples - #=20 - # body ONE_IMAGE eval:image_count('all',1,1)=20 - # body ONE_OR_MORE_IMAGES eval:image_count('all',1) -@@ -44,13 +73,24 @@ - # max: optional, if specified, message must not - # contain more than this much pixel area - # --# examples -+# pixel_coverage() examples -+# -+# body LARGE_IMAGE_AREA eval:pixel_coverage('all',150000) # = catches any images that are 150k pixel/sq or higher -+# body SMALL_GIF_AREA eval:pixel_coverage('gif',1,40000) # = catches only gifs that 1 to 40k pixel/sql -+# -+# image_name_regex() -+#=20 -+# body RULENAME eval:image_name_regex()=20 -+# regex: full quoted regexp, see examples below -+# +# image_name_regex() examples +# +# body CG_DOUBLEDOT_GIF = eval:image_name_regex('/^\w{2,9}\.\.gif$/i') # catches double dot gifs = abcd..gif ++# + # See the ruleset for ways to meta image_count()=20 + # and pixel_coverage() together. =20 # --# body LARGE_IMAGE_AREA eval:pixel_coverage('all',150000) --# body SMALL_GIF_AREA eval:pixel_coverage('gif',1,40000) -+# See the ruleset for more examples that arent documented here. - # --# See the ruleset for ways to meta image_count()=20 --# and pixel_coverage() together. =20 -+# New functions added in v0.5+ need some documentation here. Or just = -+# see .cf for sample rules. - # - # ------------------------------------------------------- -=20 -@@ -80,6 +120,7 @@ +@@ -80,6 +84,7 @@ $self->register_eval_rule ("image_size_exact"); $self->register_eval_rule ("image_size_range"); $self->register_eval_rule ("image_named"); @@ -100,10 +19,13 @@ $self->register_eval_rule ("image_to_text_ratio"); =20 return $self; -@@ -244,6 +285,34 @@ -=20 - # ----------------------------------------- +@@ -272,6 +277,33 @@ =20 + # dbg("imageinfo: pc_$type: $min, ".($max ? $max:'').", $type, = ".$pms->{'imageinfo'}->{"pc_$type"}); + return result_check($min, $max, $pms->{'imageinfo'}->{"pc_$type"}); ++} ++ ++# ----------------------------------------- +sub image_name_regex { + my ($self,$pms,$body,$re) =3D @_; + return unless (defined $re); @@ -128,20 +50,7 @@ + } + return 0; + -+} -+ -+# ----------------------------------------- -+ - sub image_count { - my ($self,$pms,$body,$type,$min,$max) =3D @_; - =20 -@@ -323,7 +392,8 @@ - $self->_get_images($pms); } =20 -- return unless (exists $pms->{'imageinfo'}->{"dems_$type"}); -+ my $name =3D 'dems_'.$type; -+ return unless (exists $pms->{'imageinfo'}->{$name}); + # ----------------------------------------- =20 - foreach my $dem ( keys %{$pms->{'imageinfo'}->{"dems_$type"}}) { - my ($h,$w) =3D split(/x/,$dem); diff -bBru /var/tmp/p5-Mail-SpamAssassin/files/patch-dnsbug-multiple = ./files/patch-dnsbug-multiple --- /var/tmp/p5-Mail-SpamAssassin/files/patch-dnsbug-multiple Fri Aug 10 = 13:47:50 2007 +++ ./files/patch-dnsbug-multiple Fri Aug 10 13:31:38 2007 @@ -0,0 +1,1170 @@ +--- Mail/SpamAssassin/AsyncLoop.pm Mon Jul 23 16:46:40 2007 ++++ lib/Mail/SpamAssassin/AsyncLoop.pm Fri Aug 10 16:10:56 2007 +@@ -43,4 +43,10 @@ + our @ISA =3D qw(); +=20 ++# Load Time::HiRes if it's available ++BEGIN { ++ eval { require Time::HiRes }; ++ Time::HiRes->import( qw(time) ) unless $@; ++} ++ + = #########################################################################= #### +=20 +@@ -52,9 +58,10 @@ + my $self =3D { + main =3D> $main, +- last_count =3D> 0, +- times_count_was_same =3D> 0, + queries_started =3D> 0, + queries_completed =3D> 0, +- pending_lookups =3D> { } ++ total_queries_started =3D> 0, ++ total_queries_completed =3D> 0, ++ pending_lookups =3D> { }, ++ timing_by_query =3D> { }, + }; +=20 +@@ -117,11 +124,25 @@ + my ($self, $ent) =3D @_; +=20 +- die "oops, no id" unless $ent->{id}; +- die "oops, no key" unless $ent->{key}; +- die "oops, no type" unless $ent->{type}; ++ die "oops, no id" unless $ent->{id} ne ''; ++ die "oops, no key" unless $ent->{key} ne ''; ++ die "oops, no type" unless $ent->{type} ne ''; ++ ++ my $now =3D time; ++ my $key =3D $ent->{key}; ++ my $id =3D $ent->{id}; ++ $ent->{start_time} =3D $now if !defined $ent->{start_time}; ++ $ent->{timeout} =3D ++ $self->{main}->{conf}->{rbl_timeout} if !defined $ent->{timeout}; ++ $ent->{display_id} =3D # identifies entry in debug logging and = similar ++ join(", ", grep { defined } ++ map { ref $ent->{$_} ? @{$ent->{$_}} : $ent->{$_} } ++ qw(sets rules rulename type key) ), +=20 + $self->{queries_started}++; +- $self->{pending_lookups}->{$ent->{key}} =3D $ent; +- $self->{last_start_lookup_time} =3D time; ++ $self->{total_queries_started}++; ++ $self->{pending_lookups}->{$key} =3D $ent; ++ ++ dbg("async: starting: %s (timeout %.1f s)", ++ $ent->{display_id}, $ent->{timeout}); + $ent; + } +@@ -165,4 +186,20 @@ + # = -------------------------------------------------------------------------= -- +=20 ++=3Ditem $async->log_lookups_timing() ++ ++Log sorted timing for all completed lookups. ++ ++=3Dcut ++ ++sub log_lookups_timing { ++ my ($self) =3D @_; ++ my $timings =3D $self->{timing_by_query}; ++ for my $key (sort { $timings->{$a} <=3D> $timings->{$b} } keys = %$timings) { ++ dbg("async: timing: %.3f %s", $timings->{$key}, $key); ++ } ++} ++ ++# = -------------------------------------------------------------------------= -- ++ + =3Ditem $alldone =3D $async->complete_lookups() +=20 +@@ -177,13 +214,12 @@ +=20 + sub complete_lookups { +- my ($self, $timeout) =3D @_; +- my %typecount =3D (); +- my $stillwaiting =3D 0; ++ my ($self, $timeout, $allow_aborting_of_expired) =3D @_; ++ my $alldone =3D 0; ++ my $anydone =3D 0; ++ my $waiting_time =3D 0; ++ my $allexpired =3D 1; ++ my %typecount; +=20 + my $pending =3D $self->{pending_lookups}; +- if (scalar keys %{$pending} <=3D 0) { +- return 1; # nothing left to do +- } +- + $self->{queries_started} =3D 0; + $self->{queries_completed} =3D 0; +@@ -193,11 +229,17 @@ + # thrown die()s before (bug 3794). + eval { ++ my $now =3D time; +=20 +- my $nfound =3D = $self->{main}->{resolver}->poll_responses($timeout); +- $nfound ||=3D 'no'; +- dbg ("async: select found $nfound socks ready"); ++ if (%$pending) { # anything to do? ++ # dbg("async: before select, timeout=3D%.1f", $timeout) if = $timeout > 0; ++ $self->{last_poll_responses_time} =3D $now; ++ my $nfound; ++ ($nfound, $waiting_time) =3D ++ $self->{main}->{resolver}->poll_responses($timeout); ++ dbg("async: select found %s responses ready", !$nfound ? 'no' : = $nfound); ++ } +=20 +- foreach my $key (keys %{$pending}) { +- my $ent =3D $pending->{$key}; ++ while (my($key,$ent) =3D each %$pending) { ++ my $id =3D $ent->{id}; +=20 + # call a "poll_callback" sub, if one exists +@@ -205,62 +247,66 @@ + $ent->{poll_callback}->($ent); + } ++ if (exists $self->{finished}->{$id}) { ++ $anydone =3D 1; ++ $ent->{finish_time} =3D $now if !defined $ent->{finish_time}; ++ $ent->{response_packet} =3D delete $self->{finished}->{$id}; +=20 +- my $type =3D $ent->{type}; +- if (!exists ($self->{finished}->{$ent->{id}})) { +- $typecount{$type}++; +- next; +- } ++ dbg("async: query completed in %.3f s: %s", ++ $ent->{finish_time} - $ent->{start_time}, = $ent->{display_id}); +=20 +- $ent->{response_packet} =3D delete = $self->{finished}->{$ent->{id}}; +- if (defined $ent->{completed_callback}) { +- $ent->{completed_callback}->($ent); ++ if (defined $ent->{completed_callback}) { ++ $ent->{completed_callback}->($ent); ++ } ++ $self->{queries_completed}++; ++ $self->{total_queries_completed}++; ++ $self->{timing_by_query}->{". $key"} +=3D ++ $ent->{finish_time} - $ent->{start_time}; ++ delete $pending->{$key}; + } +- +- $self->{queries_completed}++; +- delete $self->{pending_lookups}->{$key}; + } +=20 +- dbg("async: queries completed: ".$self->{queries_completed}. +- " started: ".$self->{queries_started}); +- +- if (1) { +- dbg("async: queries active: ". +- join (' ', map { "$_=3D$typecount{$_}" } sort keys = %typecount)." at ". +- localtime(time)); ++ if (%$pending) { # still any requests outstanding? are they = expired? ++ my $timeout_shrink_factor =3D ++ !$allow_aborting_of_expired || !$self->{total_queries_started} = ? 1.0 ++ : 1 - 0.7 * ( ($self->{total_queries_completed} / ++ $self->{total_queries_started}) ** 2 ); ++ dbg("async: timeout shrink factor: %.2f", ++ $timeout_shrink_factor) if $timeout_shrink_factor !=3D 1; ++ ++ while (my($key,$ent) =3D each %$pending) { ++ $typecount{$ent->{type}}++; ++ my $dt =3D $ent->{timeout} * $timeout_shrink_factor; ++ $dt =3D 1.0 if $dt < 1.0; # don't shrink allowed time below = 1 second ++ $allexpired =3D 0 if $now <=3D $ent->{start_time} + $dt; ++ } ++ dbg("async: queries completed: %d, started: %d", ++ $self->{queries_completed}, $self->{queries_started}); + } +=20 + # ensure we don't get stuck if a request gets lost in the ether. +- if (!$stillwaiting) { +- my $numkeys =3D scalar keys %{$self->{pending_lookups}}; +- if ($numkeys =3D=3D 0) { +- $stillwaiting =3D 0; +- +- } else { +- $stillwaiting =3D 1; +- +- # avoid looping forever if we haven't got all results. +- if ($self->{last_count} =3D=3D $numkeys) { +- $self->{times_count_was_same}++; +- if ($self->{times_count_was_same} > 20) +- { +- dbg("async: escaping: must have lost requests"); +- $self->abort_remaining_lookups(); +- $stillwaiting =3D 0; +- } +- } else { +- $self->{last_count} =3D $numkeys; +- $self->{times_count_was_same} =3D 0; +- } +- } ++ if (! %$pending) { ++ $alldone =3D 1; ++ } ++ elsif ($allexpired && $allow_aborting_of_expired) { ++ # avoid looping forever if we haven't got all results. ++ dbg("async: escaping: lost or timed out requests or responses"); ++ $self->abort_remaining_lookups(); ++ $alldone =3D 1; + } ++ else { ++ dbg("async: queries active: %s%s at %s", ++ join (' ', map { "$_=3D$typecount{$_}" } sort keys = %typecount), ++ $allexpired ? ', all expired' : '', = scalar(localtime(time))); ++ $alldone =3D 0; ++ } ++ 1; +=20 ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp $eval_stat; ++ dbg("async: caught complete_lookups death, aborting: %s", = $eval_stat); ++ $alldone =3D 1; # abort remaining + }; +=20 +- if ($@) { +- dbg("async: caught complete_lookups death, aborting: $@"); +- $stillwaiting =3D 0; # abort remaining +- } +- +- return (!$stillwaiting); ++ return wantarray ? ($alldone,$anydone,$waiting_time) : $alldone; + } +=20 +@@ -277,16 +323,20 @@ +=20 + my $pending =3D $self->{pending_lookups}; +- my $foundone =3D 0; +- foreach my $key (keys %{$pending}) +- { +- if (!$foundone) { +- dbg("async: aborting remaining lookups"); +- $foundone =3D 1; +- } +- ++ my $foundcnt =3D 0; ++ my $now =3D time; ++ while (my($key,$ent) =3D each %$pending) { ++ dbg("async: aborting after %.3f s, %s: %s", ++ $now - $ent->{start_time}, ++ defined $ent->{timeout} && $now > $ent->{start_time} + = $ent->{timeout} ++ ? 'past original deadline' : 'shrunk deadline', ++ $ent->{display_id} ); ++ $foundcnt++; ++ $self->{timing_by_query}->{"X $key"} =3D $now - = $ent->{start_time}; + delete $pending->{$key}; + } +- delete $self->{last_start_lookup_time}; ++ dbg("async: aborted %d remaining lookups", $foundcnt) if $foundcnt = > 0; ++ delete $self->{last_poll_responses_time}; + $self->{main}->{resolver}->bgabort(); ++ 1; + } +=20 +@@ -308,9 +358,15 @@ +=20 + sub set_response_packet { +- my ($self, $id, $pkt) =3D @_; ++ my ($self, $id, $pkt, $key, $timestamp) =3D @_; + $self->{finished}->{$id} =3D $pkt; ++ $timestamp =3D time if !defined $timestamp; ++ my $ent =3D $self->{pending_lookups}->{$key}; ++ $id eq $ent->{id} ++ or die "set_response_packet: PANIC - mismatched id $id, = $ent->{id}"; ++ $ent->{finish_time} =3D $timestamp; ++ 1; + } +=20 +-=3Ditem $async->report_id_complete($id) ++=3Ditem $async->report_id_complete($id,$key) +=20 + Register that a query has completed, and is no longer "pending". = C<$id> is the +@@ -323,24 +379,29 @@ +=20 + sub report_id_complete { +- my ($self, $id) =3D @_; ++ my ($self, $id, $key, $timestamp) =3D @_; + $self->{finished}->{$id} =3D undef; ++ $timestamp =3D time if !defined $timestamp; ++ my $ent =3D $self->{pending_lookups}->{$key}; ++ $id eq $ent->{id} ++ or die "report_id_complete: PANIC - mismatched id $id, = $ent->{id}"; ++ $ent->{finish_time} =3D $timestamp; ++ 1; + } +=20 + # = -------------------------------------------------------------------------= -- +=20 +-=3Ditem $time =3D $async->get_last_start_lookup_time() ++=3Ditem $time =3D $async->last_poll_responses_time() +=20 +-Get the time of the last call to C. If = C was +-never called or C has been called +-C will return undef. ++Get the time of the last call to C (which is called ++from C was never called = or ++C has been called = C ++will return undef. +=20 + =3Dcut +=20 +-sub get_last_start_lookup_time { ++sub last_poll_responses_time { + my ($self) =3D @_; +- return $self->{last_start_lookup_time}; ++ return $self->{last_poll_responses_time}; + } =20 +- +-# = -------------------------------------------------------------------------= -- +=20 + 1; +--- Mail/SpamAssassin/Dns.pm Mon Jul 23 16:46:40 2007 ++++ lib/Mail/SpamAssassin/Dns.pm Fri Aug 10 01:53:40 2007 +@@ -104,8 +104,9 @@ + # only make a specific query once + if (!$existing) { +- dbg("dns: launching DNS $type query for $host in background"); ++ dbg("dns: launching DNS %s query for %s in background", = $type,$host); +=20 + my $ent =3D { + key =3D> $key, ++ timeout =3D> $self->{conf}->{rbl_timeout}, + type =3D> "DNSBL-".$type, + sets =3D> [ ], # filled in below +@@ -115,8 +116,7 @@ +=20 + my $id =3D $self->{resolver}->bgsend($host, $type, undef, sub { +- my $pkt =3D shift; +- my $id =3D shift; ++ my ($pkt, $id, $timestamp) =3D @_; + $self->process_dnsbl_result($ent, $pkt); +- $self->{async}->report_id_complete($id); ++ $self->{async}->report_id_complete($id,$key,$timestamp); + }); +=20 +@@ -153,8 +153,9 @@ + return if $self->{async}->get_lookup($key); +=20 +- dbg("dns: launching DNS $type query for $host in background"); ++ dbg("dns: launching DNS %s query for %s in background", = $type,$host); +=20 + my $ent =3D { + key =3D> $key, ++ timeout =3D> $self->{conf}->{rbl_timeout}, + type =3D> "DNSBL-".$type, + rules =3D> [ $rule ], +@@ -163,8 +164,7 @@ +=20 + my $id =3D $self->{resolver}->bgsend($host, $type, undef, sub { +- my $pkt =3D shift; +- my $id =3D shift; ++ my ($pkt, $id, $timestamp) =3D @_; + $self->process_dnsbl_result($ent, $pkt); +- $self->{async}->report_id_complete($id); ++ $self->{async}->report_id_complete($id,$key,$timestamp); + }); +=20 +@@ -223,8 +223,9 @@ + push @{ $self->{dnsuri}->{$uri} }, $rdatastr; +=20 +- dbg ("dns: hit <$uri> $rdatastr"); ++ dbg("dns: hit <%s> %s", $uri,$rdatastr); + } + } +=20 ++# called as a completion routine to bgsend by = DnsResolver::poll_responses; + # returns 1 on successful packet processing + sub process_dnsbl_result { +@@ -285,5 +286,5 @@ + # SB rules are not available to users + if ($self->{conf}->{user_defined_rules}->{$rule}) { +- dbg("dns: skipping rule '$rule': not supported when = user-defined"); ++ dbg("dns: skipping rule '%s': not supported when = user-defined", $rule); + next; + } +@@ -329,41 +330,25 @@ + my ($self, $rule) =3D @_; +=20 +- return if !defined $self->{async}->get_last_start_lookup_time(); ++ dbg("dns: harvest_until_rule_completes"); ++ my $result =3D 0; ++ my $total_waiting_time =3D 0; ++ ++ for (my $first=3D1; ; $first=3D0) { ++ # complete_lookups() may call completed_callback(), which may ++ # call start_lookup() again (like in Plugin::URIDNSBL) ++ my ($alldone,$anydone,$waiting_time) =3D ++ $self->{async}->complete_lookups($first ? 0 : 1.0, 1); ++ $total_waiting_time +=3D $waiting_time; +=20 +- my $deadline =3D $self->{conf}->{rbl_timeout} + = $self->{async}->get_last_start_lookup_time(); +- my $now =3D time; +- +- # should not give up before at least attempting to collect some = responses +- # even if previous checks already exceeded rbl_timeout +- my $notbefore =3D $now + 1.2; # at least 1 second from now (time is = integer) +- +- my @left =3D $self->{async}->get_pending_lookups(); +- my $total =3D scalar @left; +- +- while ( (($now < $deadline) || ($now < $notbefore)) && +- !$self->{async}->complete_lookups(1)) +- { +- dbg(sprintf("dns: harvest_until_rule_completes: on extended time, = ". +- "overdue by %.3f s, still %.3f s", +- $now-$deadline, $notbefore-$now)) if $now >=3D $deadline; +- +- if ($self->is_rule_complete($rule)) { +- return 1; +- } ++ $result =3D 1 if $self->is_rule_complete($rule); ++ last if $result || $alldone; +=20 ++ dbg("dns: harvest_until_rule_completes - check_tick"); + $self->{main}->call_plugins ("check_tick", { permsgstatus =3D> = $self }); +- @left =3D $self->{async}->get_pending_lookups(); +- +- # complete_lookups could cause a change in = get_last_start_lookup_time +- $deadline =3D $self->{conf}->{rbl_timeout} + +- $self->{async}->get_last_start_lookup_time(); +- +- # dynamic timeout +- my $dynamic =3D (int($self->{conf}->{rbl_timeout} +- * (1 - 0.7*(($total - @left) / $total) ** 2) + = 1) +- + $self->{async}->get_last_start_lookup_time()); +- $deadline =3D $dynamic if ($dynamic < $deadline); +- $now =3D time; + } ++ dbg("dns: timing: %.3f s sleeping in harvest_until_rule_completes", ++ $total_waiting_time) if $total_waiting_time > 0; ++ ++ return $result; + } +=20 +@@ -371,57 +356,50 @@ + my ($self) =3D @_; +=20 +- return if !defined $self->{async}->get_last_start_lookup_time(); ++ dbg("dns: harvest_dnsbl_queries"); ++ my $total_waiting_time =3D 0; +=20 +- my $deadline =3D $self->{conf}->{rbl_timeout} + = $self->{async}->get_last_start_lookup_time(); +- my $now =3D time; ++ for (my $first=3D1; ; $first=3D0) { +=20 +- # should not give up before at least attempting to collect some = responses +- # (which may have arrived by now), even if previous checks (like = Razor, +- # dcc, Botnet, rules) already exceeded rbl_timeout +- my $notbefore =3D $now + 1.2; # at least 1 second from now (time is = integer) ++ # complete_lookups() may call completed_callback(), which may ++ # call start_lookup() again (like in Plugin::URIDNSBL) +=20 +- my @left =3D $self->{async}->get_pending_lookups(); +- my $total =3D scalar @left; ++ # the first time around we specify a 0 timeout, which gives ++ # complete_lookups a chance to ripe any available results and ++ # abort overdue requests, without needlessly waiting for more +=20 +- while ( (($now < $deadline) || ($now < $notbefore)) && +- !$self->{async}->complete_lookups(1)) +- { +- dbg(sprintf("dns: harvest_dnsbl_queries: on extended time, ". +- "overdue by %.3f s, still %.3f s", +- $now-$deadline, $notbefore-$now)) if $now >=3D $deadline; ++ my ($alldone,$anydone,$waiting_time) =3D ++ $self->{async}->complete_lookups($first ? 0 : 1.0, 1); ++ $total_waiting_time +=3D $waiting_time; +=20 +- $self->{main}->call_plugins ("check_tick", { permsgstatus =3D> = $self }); +- @left =3D $self->{async}->get_pending_lookups(); ++ last if $alldone; +=20 +- # complete_lookups() may have called completed_callback, which may = call +- # start_lookup() again (like in URIDNSBL), so = get_last_start_lookup_time +- # may have changed and deadline needs to be recomputed +- $deadline =3D $self->{conf}->{rbl_timeout} + +- $self->{async}->get_last_start_lookup_time(); +- +- # dynamic timeout +- my $dynamic =3D (int($self->{conf}->{rbl_timeout} +- * (1 - 0.7*(($total - @left) / $total) ** 2) + = 1) +- + $self->{async}->get_last_start_lookup_time()); +- $deadline =3D $dynamic if ($dynamic < $deadline); +- $now =3D time; # and loop again +- } +- +- dbg("dns: success for " . ($total - @left) . " of $total queries"); +- +- # timeouts +- @left =3D $self->{async}->get_pending_lookups(); +- $now =3D time; +- for my $query (@left) { +- my $string =3D join(", ", grep { defined } +- map { ref $query->{$_} ? @{$query->{$_}} : = $query->{$_} } +- qw(sets rules rulename type key) ); +- my $delay =3D $now - $self->{async}->get_last_start_lookup_time(); +- dbg("dns: timeout for $string after $delay seconds"); ++ dbg("dns: harvest_dnsbl_queries - check_tick"); ++ $self->{main}->call_plugins ("check_tick", { permsgstatus =3D> = $self }); + } +=20 +- # and explicitly abort anything left ++ # explicitly abort anything left + $self->{async}->abort_remaining_lookups(); ++ $self->{async}->log_lookups_timing(); + $self->mark_all_async_rules_complete(); ++ dbg("dns: timing: %.3f s sleeping in harvest_dnsbl_queries", ++ $total_waiting_time) if $total_waiting_time > 0; ++ 1; ++} ++ ++# collect and process whatever DNS responses have already arrived, ++# don't waste time waiting for more, don't poll too often. ++# don't abort any queries even if overdue,=20 ++sub harvest_completed_queries { ++ my ($self) =3D @_; ++ ++ # don't bother collecting responses too often ++ my $last_poll_time =3D $self->{async}->last_poll_responses_time(); ++ return if defined $last_poll_time && time - $last_poll_time < 0.1; ++ ++ my ($alldone,$anydone) =3D $self->{async}->complete_lookups(0, 0); ++ if ($anydone) { ++ dbg("dns: harvested completed queries"); ++# $self->{main}->call_plugins ("check_tick", { permsgstatus =3D> = $self }); ++ } + } +=20 +@@ -466,5 +444,5 @@ +=20 + my $nsrecords; +- dbg("dns: looking up NS for '$dom'"); ++ dbg("dns: looking up NS for '%s'", $dom); +=20 + if (exists $self->{dnscache}->{NS}->{$dom}) { +@@ -474,5 +452,5 @@ + eval { + my $query =3D $self->{resolver}->send($dom, 'NS'); +- my @nses =3D (); ++ my @nses; + if ($query) { + foreach my $rr ($query->answer) { +@@ -481,9 +459,10 @@ + } + $nsrecords =3D $self->{dnscache}->{NS}->{$dom} =3D [ @nses ]; +- }; +- if ($@) { +- dbg("dns: NS lookup failed horribly, perhaps bad resolv.conf = setting? ($@)"); ++ 1; ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp = $eval_stat; ++ dbg("dns: NS lookup failed horribly, perhaps bad resolv.conf = setting? (%s)", $eval_stat); + return undef; +- } ++ }; + } +=20 +@@ -498,5 +477,5 @@ +=20 + my $mxrecords; +- dbg("dns: looking up MX for '$dom'"); ++ dbg("dns: looking up MX for '%s'", $dom); +=20 + if (exists $self->{dnscache}->{MX}->{$dom}) { +@@ -506,5 +485,5 @@ + eval { + my $query =3D $self->{resolver}->send($dom, 'MX'); +- my @ips =3D (); ++ my @ips; + if ($query) { + foreach my $rr ($query->answer) { +@@ -513,11 +492,11 @@ + } + } +- + $mxrecords =3D $self->{dnscache}->{MX}->{$dom} =3D [ @ips ]; +- }; +- if ($@) { +- dbg("dns: MX lookup failed horribly, perhaps bad resolv.conf = setting? ($@)"); ++ 1; ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp = $eval_stat; ++ dbg("dns: MX lookup failed horribly, perhaps bad resolv.conf = setting? (%s)", $eval_stat); + return undef; +- } ++ }; + } +=20 +@@ -533,5 +512,5 @@ + if (scalar @{$recs}) { $ret =3D 1; } +=20 +- dbg("dns: MX for '$dom' exists? $ret"); ++ dbg("dns: MX for '%s' exists? %s", $dom,$ret); + return $ret; + } +@@ -549,5 +528,5 @@ +=20 + if ($dom =3D~ /${IP_PRIVATE}/) { +- dbg("dns: IP is private, not looking up PTR: $dom"); ++ dbg("dns: IP is private, not looking up PTR: %s", $dom); + return undef; + } +@@ -555,5 +534,5 @@ + return if ($self->server_failed_to_respond_for_domain ($dom)); +=20 +- dbg("dns: looking up PTR record for '$dom'"); ++ dbg("dns: looking up PTR record for '%s'", $dom); + my $name =3D ''; +=20 +@@ -571,14 +550,13 @@ + } + } +- + $name =3D $self->{dnscache}->{PTR}->{$dom} =3D $name; +- }; +- +- if ($@) { +- dbg("dns: PTR lookup failed horribly, perhaps bad resolv.conf = setting? ($@)"); ++ 1; ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp = $eval_stat; ++ dbg("dns: PTR lookup failed horribly, perhaps bad resolv.conf = setting? (%s)", $eval_stat); + return undef; +- } ++ }; + } +- dbg("dns: PTR for '$dom': '$name'"); ++ dbg("dns: PTR for '%s': '%s'", $dom,$name); +=20 + # note: undef is never returned, unless DNS is unavailable. +@@ -597,6 +575,6 @@ + return if ($self->server_failed_to_respond_for_domain ($name)); +=20 +- dbg("dns: looking up A records for '$name'"); +- my @addrs =3D (); ++ dbg("dns: looking up A records for '%s'", $name); ++ my @addrs; +=20 + if (exists $self->{dnscache}->{A}->{$name}) { +@@ -615,13 +593,13 @@ + } + $self->{dnscache}->{A}->{$name} =3D [ @addrs ]; +- }; +- +- if ($@) { +- dbg("dns: A lookup failed horribly, perhaps bad resolv.conf = setting? ($@)"); ++ 1; ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp = $eval_stat; ++ dbg("dns: A lookup failed horribly, perhaps bad resolv.conf = setting? (%s)", $eval_stat); + return undef; +- } ++ }; + } +=20 +- dbg("dns: A records for '$name': ".join (' ', @addrs)); ++ dbg("dns: A records for '%s': %s", $name, join(' ',@addrs)); + return @addrs; + } +@@ -640,5 +618,6 @@ + if ($dnsopt eq "test" && $diff > $dnsint) { + $IS_DNS_AVAILABLE =3D undef; +- dbg("dns: is_dns_available() last checked $diff seconds ago; = re-checking"); ++ dbg("dns: is_dns_available() last checked %.1f seconds ago; = re-checking", ++ $diff); + } +=20 +@@ -682,7 +661,8 @@ + if ($dnsopt =3D~ /test:\s+(.+)$/) { + my $servers=3D$1; +- dbg("dns: servers: $servers"); ++ dbg("dns: servers: %s", $servers); + @domains =3D split (/\s+/, $servers); +- dbg("dns: looking up NS records for user specified servers: = ".join(", ", @domains)); ++ dbg("dns: looking up NS records for user specified servers: %s", ++ join(", ", @domains)); + } else { + @domains =3D @EXISTING_DOMAINS; +@@ -698,24 +678,27 @@ + =20 + my @nameservers =3D $self->{resolver}->nameservers(); +- dbg("dns: testing resolver nameservers: ".join(", ", @nameservers)); ++ dbg("dns: testing resolver nameservers: %s", join(", ", = @nameservers)); + my $ns; + while( $ns =3D shift(@nameservers)) { + for(my $retry =3D 3; $retry > 0 and $#domains>-1; $retry--) { + my $domain =3D splice(@domains, rand(@domains), 1); +- dbg("dns: trying ($retry) $domain..."); ++ dbg("dns: trying (%d) %s...", $retry,$domain); + my $result =3D $self->lookup_ns($domain); + if(defined $result) { + if (scalar @$result > 0) { +- dbg("dns: NS lookup of $domain using $ns succeeded =3D> DNS = available (set dns_available to override)"); ++ dbg("dns: NS lookup of %s using %s succeeded =3D> DNS = available ". ++ "(set dns_available to override)", $domain,$ns); + $IS_DNS_AVAILABLE =3D 1; + last; + } + else { +- dbg("dns: NS lookup of $domain using $ns failed, no results = found"); ++ dbg("dns: NS lookup of %s using %s failed, no results = found", ++ $domain,$ns); + next; + } + } + else { +- dbg("dns: NS lookup of $domain using $ns failed horribly, may = not be a valid nameserver"); ++ dbg("dns: NS lookup of %s using %s failed horribly, ". ++ "may not be a valid nameserver", $domain,$ns); + $IS_DNS_AVAILABLE =3D 0; # should already be 0, but let's be = sure. + last;=20 +@@ -723,5 +706,5 @@ + } + last if $IS_DNS_AVAILABLE; +- dbg("dns: NS lookups failed, removing nameserver $ns from list"); ++ dbg("dns: NS lookups failed, removing nameserver %s from list", = $ns); + $self->{resolver}->nameservers(@nameservers); + $self->{resolver}->connect_sock(); # reconnect socket to new = nameserver +@@ -732,5 +715,5 @@ + done: + # jm: leaving this in! +- dbg("dns: is DNS available? $IS_DNS_AVAILABLE"); ++ dbg("dns: is DNS available? %s", $IS_DNS_AVAILABLE); + return $IS_DNS_AVAILABLE; + } +@@ -741,5 +724,6 @@ + my ($self, $dom) =3D @_; + if ($self->{dns_server_too_slow}->{$dom}) { +- dbg("dns: server for '$dom' failed to reply previously, not asking = again"); ++ dbg("dns: server for '%s' failed to reply previously, not asking = again", ++ $dom); + return 1; + } +@@ -749,5 +733,5 @@ + sub set_server_failed_to_respond_for_domain { + my ($self, $dom) =3D @_; +- dbg("dns: server for '$dom' failed to reply, marking as bad"); ++ dbg("dns: server for '%s' failed to reply, marking as bad", $dom); + $self->{dns_server_too_slow}->{$dom} =3D 1; + } +@@ -823,5 +807,5 @@ + sub register_async_rule_start { + my ($self, $rule) =3D @_; +- dbg ("dns: $rule lookup start"); ++ dbg("dns: %s lookup start", $rule); + $self->{rule_to_rblkey}->{$rule} =3D '*ASYNC_START'; + } +@@ -829,5 +813,5 @@ + sub register_async_rule_finish { + my ($self, $rule) =3D @_; +- dbg ("dns: $rule lookup finished"); ++ dbg("dns: %s lookup finished", $rule); + delete $self->{rule_to_rblkey}->{$rule}; + } +@@ -843,10 +827,10 @@ + my $key =3D $self->{rule_to_rblkey}->{$rule}; + if (!defined $key) { +- # dbg("dns: $rule lookup complete, not in list"); ++ # dbg("dns: %s lookup complete, not in list", $rule); + return 1; + } +=20 + if ($key eq '*ASYNC_START') { +- dbg("dns: $rule lookup not yet complete"); ++ dbg("dns: %s lookup not yet complete", $rule); + return 0; # not yet complete + } +@@ -854,9 +838,9 @@ + my $obj =3D $self->{async}->get_lookup($key); + if (!defined $obj) { +- dbg("dns: $rule lookup complete, $key no longer pending"); ++ dbg("dns: %s lookup complete, $key no longer pending", $rule); + return 1; + } +=20 +- dbg("dns: $rule lookup not yet complete"); ++ dbg("dns: %s lookup not yet complete", $rule); + return 0; # not yet complete + } +--- Mail/SpamAssassin/DnsResolver.pm Mon Jul 23 16:46:40 2007 ++++ lib/Mail/SpamAssassin/DnsResolver.pm Fri Aug 10 11:48:34 2007 +@@ -50,4 +50,10 @@ + our @ISA =3D qw(); +=20 ++# Load Time::HiRes if it's available ++BEGIN { ++ eval { require Time::HiRes }; ++ Time::HiRes->import( qw(time) ) unless $@; ++} ++ + = #########################################################################= ## +=20 +@@ -131,8 +137,8 @@ +=20 + dbg("dns: no ipv6") if $force_ipv4; +- dbg("dns: is Net::DNS::Resolver available? " . +- ($self->{no_resolver} ? "no" : "yes")); ++ dbg("dns: is Net::DNS::Resolver available? %s", ++ $self->{no_resolver} ? "no" : "yes" ); + if (!$self->{no_resolver} && defined $Net::DNS::VERSION) { +- dbg("dns: Net::DNS version: ".$Net::DNS::VERSION); ++ dbg("dns: Net::DNS version: %s", $Net::DNS::VERSION); + } +=20 +@@ -197,5 +203,5 @@ + } +=20 +- dbg("dns: name server: $ns, LocalAddr: $srcaddr"); ++ dbg("dns: name server: %s, LocalAddr: %s", $ns,$srcaddr); +=20 + # find next available unprivileged port (1024 - 65535) +@@ -223,5 +229,5 @@ + last; + } elsif ($! =3D=3D EADDRINUSE || $! =3D=3D EACCES) { # in use, = let's try another source port +- dbg("dns: UDP port $lport already in use, trying another port"); ++ dbg("dns: UDP port %s already in use, trying another port", = $lport); + } else { + warn "Error creating a DNS resolver socket: $errno"; +@@ -293,12 +299,14 @@ +=20 + # a bit noisy, so commented by default... +- #dbg("dns: new DNS packet time=3D".time()." host=3D$host = type=3D$type id=3D".$packet->id); +- }; +- +- if ($@) { ++ #dbg("dns: new DNS packet time=3D%s host=3D%s type=3D%s id=3D%s", ++ # time, $host, $type, $packet->id); ++ 1; ++ } or do { ++ my $eval_stat =3D $@ ne '' ? $@ : "errno=3D$!"; chomp $eval_stat; + # this can happen if Net::DNS isn't available -- but in this + # case this function should never be called! +- warn "dns: cannot create Net::DNS::Packet, but new_dns_packet() = was called: $@ $!"; +- } ++ warn "dns: cannot create Net::DNS::Packet, but new_dns_packet() = was called: $eval_stat"; ++ }; ++ + return $packet; + } +@@ -339,16 +347,17 @@ + will default to C and C, respectively. +=20 +-The callback sub will be called with two arguments -- the packet that = was +-delivered and an id string that fingerprints the query packet and the = expected reply. +-It is expected that a closure callback be used, like so: ++The callback sub will be called with three arguments -- the packet = that was ++delivered, and an id string that fingerprints the query packet and the = expected ++reply. The third argument is a timestamp (Unix time, floating point), = captured ++at the time the packet was collected. It is expected that a closure = callback ++be used, like so: +=20 + my $id =3D $self->{resolver}->bgsend($host, $type, undef, sub { +- my $reply =3D shift; +- my $reply_id =3D shift; ++ my ($reply, $reply_id, $timestamp) =3D @_; + $self->got_a_reply ($reply, $reply_id); + }); +=20 +-The callback can ignore the reply as an invalid packet sent to the = listening port +-if the reply id does not match the return value from bgsend. ++The callback can ignore the reply as an invalid packet sent to the = listening ++port if the reply id does not match the return value from bgsend. +=20 + =3Dcut +@@ -385,44 +394,51 @@ + return if $self->{no_resolver}; + return if !$self->{sock}; ++ my $cnt =3D 0; ++ my $waiting_time =3D 0; +=20 + my $rin =3D $self->{sock_as_vec}; + my $rout; +- my ($nfound, $timeleft) =3D select($rout=3D$rin, undef, undef, = $timeout); +=20 +- if (!defined $nfound || $nfound < 0) { +- warn "dns: select failed: $!"; +- return; +- } ++ for (;;) { ++ my $now_before =3D time; ++ my ($nfound, $timeleft) =3D select($rout=3D$rin, undef, undef, = $timeout); ++ if (!defined $nfound || $nfound < 0) { ++ warn "dns: select failed: $!"; ++ return; ++ } +=20 +- if ($nfound =3D=3D 0) { +- return 0; # nothing's ready yet +- } ++ my $now =3D time; ++ if ($now > $now_before && (!defined($timeout) || $timeout > 0)) { ++ $waiting_time +=3D $now - $now_before; ++ } ++ $timeout =3D 0; # next time around collect whatever is available, = then exit ++ last if $nfound =3D=3D 0; +=20 +- my $packet =3D $self->{res}->bgread($self->{sock}); +- my $err =3D $self->{res}->errorstring; ++ my $packet =3D $self->{res}->bgread($self->{sock}); ++ my $err =3D $self->{res}->errorstring; +=20 +- if (defined $packet && +- defined $packet->header && +- defined $packet->question && +- defined $packet->answer) +- { +- my $id =3D $self->_packet_id($packet); +- +- my $cb =3D delete $self->{id_to_callback}->{$id}; +- if (!$cb) { +- dbg("dns: no callback for id: $id, ignored; packet: ". +- ($packet ? $packet->string : "undef")); +- return 0; ++ if (defined $packet && ++ defined $packet->header && ++ defined $packet->question && ++ defined $packet->answer) ++ { ++ my $id =3D $self->_packet_id($packet); ++ ++ my $cb =3D delete $self->{id_to_callback}->{$id}; ++ if (!$cb) { ++ dbg("dns: no callback for id: %s, ignored; packet: %s", ++ $id, $packet ? $packet->string : "undef" ); ++ } else { ++ $cb->($packet, $id, $now); ++ $cnt++; ++ } ++ } ++ else { ++ dbg("dns: no packet! err=3D%s packet=3D%s", ++ $err, $packet ? $packet->string : "undef" ); + } +- +- $cb->($packet, $id); +- return 1; +- } +- else { +- dbg("dns: no packet! err=3D$err packet=3D". +- ($packet ? $packet->string : "undef")); + } +=20 +- return 0; ++ return wantarray ? ($cnt, $waiting_time) : $cnt; + } +=20 +@@ -458,4 +474,5 @@ + my $timeout =3D $retrans; + my $answerpkt; ++ my $answerpkt_avail =3D 0; + for (my $i =3D 0; + (($i < $retries) && !defined($answerpkt)); +@@ -465,5 +482,6 @@ + # note nifty use of a closure here. I love closures ;) + $self->bgsend($name, $type, $class, sub { +- $answerpkt =3D shift; ++ my ($reply, $reply_id, $timestamp) =3D @_; ++ $answerpkt =3D $reply; $answerpkt_avail =3D 1; + }); +=20 +@@ -471,10 +489,9 @@ + my $deadline =3D $now + $timeout; +=20 +- while (($now < $deadline) && (!defined($answerpkt))) { ++ while (!$answerpkt_avail) { ++ if ($now >=3D $deadline) { $self->{send_timed_out} =3D 1; last } + $self->poll_responses(1); +- last if defined $answerpkt; + $now =3D time; + } +- $self->{send_timed_out} =3D 1 unless ($now < $deadline); + } + return $answerpkt; +@@ -540,6 +557,9 @@ + foreach my $sock (@fhlist) { + my $fno =3D fileno($sock); +- warn "dns: oops! fileno now undef for $sock" unless defined($fno); +- vec ($rin, $fno, 1) =3D 1; ++ if (!defined $fno) { ++ warn "dns: oops! fileno now undef for $sock"; ++ } else { ++ vec ($rin, $fno, 1) =3D 1; ++ } + } + return $rin; +--- Mail/SpamAssassin/Logger.pm Mon Jul 23 16:46:40 2007 ++++ lib/Mail/SpamAssassin/Logger.pm Tue Aug 7 16:34:30 2007 +@@ -195,7 +195,7 @@ + # remember to avoid deep recursion, my friend + sub _log { +- my ($level, $message) =3D @_; +- ++ my ($level, $message, @args) =3D @_; + my $facility =3D "generic"; ++ local ($1,$2); + if ($message =3D~ /^(\S+?): (.*)/s) { + $facility =3D $1; +@@ -210,4 +210,5 @@ + } +=20 ++ if (@args && index($message,'%') >=3D 0) { $message =3D = sprintf($message,@args) } + $message =3D~ s/\n+$//s; + $message =3D~ s/^/${facility}: /mg; +--- Mail/SpamAssassin/Plugin/ASN.pm Mon Jul 23 16:46:39 2007 ++++ lib/Mail/SpamAssassin/Plugin/ASN.pm Fri Aug 10 12:30:40 2007 +@@ -191,5 +191,5 @@ + if (defined $relay->{ip} && $relay->{ip} =3D~ = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + $reversed_ip_quad =3D "$4.$3.$2.$1"; +- dbg("asn: using first external relay IP for lookups: = $relay->{ip}"); ++ dbg("asn: using first external relay IP for lookups: %s", = $relay->{ip}); + } else { + dbg("asn: could not parse IP from first external relay, skipping = ASN check"); +@@ -213,11 +213,17 @@ + # do the DNS query, have the callback process the result rather = than poll for them later + my $zone_index =3D $index; ++ my $key =3D "asnlookup-${zone_index}-$entry->{zone}"; + my $id =3D = $scanner->{main}->{resolver}->bgsend("${reversed_ip_quad}.$entry->{zone}"= , 'TXT', undef, sub { +- my $pkt =3D shift; ++ my ($pkt, $id, $timestamp) =3D @_; ++ $scanner->{async}->set_response_packet($id, $pkt, $key, = $timestamp); + $self->process_dns_result($scanner, $pkt, $zone_index); + }); +- +- $scanner->{async}->start_lookup({ = key=3D>"asnlookup-${zone_index}-$entry->{zone}", id=3D>$id, = type=3D>'TXT' }); +- dbg("asn: launched DNS TXT query for = ${reversed_ip_quad}.$entry->{zone} in background"); ++ my $ent =3D { ++ key=3D>$key, id=3D>$id, type=3D>'TXT', ++ # timeout =3D> ... # defaults to $scanner->{conf}->{rbl_timeout} ++ }; ++ $scanner->{async}->start_lookup($ent); ++ dbg("asn: launched DNS TXT query for %s.%s in background", ++ $reversed_ip_quad, $entry->{zone}); +=20 + $index++; +@@ -237,9 +243,10 @@ +=20 + foreach my $rr (@answer) { +- dbg("asn: $zone: lookup result packet: '".$rr->string."'"); ++ dbg("asn: %s: lookup result packet: '%s'", $zone, $rr->string); + if ($rr->type eq 'TXT') { + my @items =3D split(/ /, $rr->txtdata); + unless ($#items =3D=3D 2) { +- dbg("asn: TXT query response format unknown, ignoring zone: = $zone response: '".$rr->txtdata."'"); ++ dbg("asn: TXT query response format unknown, ignoring zone: = %s, ". ++ "response: '%s'", $zone, $rr->txtdata); + next; + } +--- Mail/SpamAssassin/Plugin/Check.pm Mon Jul 23 16:46:39 2007 ++++ lib/Mail/SpamAssassin/Plugin/Check.pm Wed Aug 8 16:50:40 2007 +@@ -92,4 +92,5 @@ + } +=20 ++ $pms->harvest_completed_queries(); + # allow other, plugin-defined rule types to be called here + $self->{main}->call_plugins ("check_rules_at_priority", +@@ -98,21 +99,32 @@ + # do head tests + $self->do_head_tests($pms, $priority); ++ $pms->harvest_completed_queries(); + $self->do_head_eval_tests($pms, $priority); ++ $pms->harvest_completed_queries(); +=20 + $self->do_body_tests($pms, $priority, $decoded); ++ $pms->harvest_completed_queries(); + $self->do_uri_tests($pms, $priority, @uris); ++ $pms->harvest_completed_queries(); + $self->do_body_eval_tests($pms, $priority, $decoded); ++ $pms->harvest_completed_queries(); + =20 + $self->do_rawbody_tests($pms, $priority, $bodytext); ++ $pms->harvest_completed_queries(); + $self->do_rawbody_eval_tests($pms, $priority, $bodytext); ++ $pms->harvest_completed_queries(); + =20 + $self->do_full_tests($pms, $priority, \$fulltext); ++ $pms->harvest_completed_queries(); + $self->do_full_eval_tests($pms, $priority, \$fulltext); ++ $pms->harvest_completed_queries(); +=20 + $self->do_meta_tests($pms, $priority); ++ $pms->harvest_completed_queries(); +=20 + # 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 =3D> = $pms }); ++ $pms->harvest_completed_queries(); + } +=20 +--- Mail/SpamAssassin/Plugin/URIDNSBL.pm Mon Jul 23 16:46:39 2007 ++++ lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm Thu Aug 9 17:34:51 2007 +@@ -474,5 +474,7 @@ +=20 + # dig $dom ns +- my $ent =3D $self->start_lookup ($scanner, 'NS', = $self->res_bgsend($scanner, $dom, 'NS'), $key); ++ my $ent =3D $self->start_lookup($scanner, 'NS', ++ $self->res_bgsend($scanner, $dom, = 'NS', $key), ++ $key); + $ent->{obj} =3D $obj; + } +@@ -518,5 +520,7 @@ +=20 + # dig $hname a +- my $ent =3D $self->start_lookup ($scanner, 'A', = $self->res_bgsend($scanner, $hname, 'A'), $key); ++ my $ent =3D $self->start_lookup($scanner, 'A', ++ $self->res_bgsend($scanner, $hname, = 'A', $key), ++ $key); + $ent->{obj} =3D $obj; + } +@@ -559,6 +563,7 @@ +=20 + # dig $ip txt +- my $ent =3D $self->start_lookup ($scanner, 'DNSBL', +- $self->res_bgsend($scanner, $item, $qtype), $key); ++ my $ent =3D $self->start_lookup($scanner, 'DNSBL', ++ $self->res_bgsend($scanner, $item, = $qtype, $key), ++ $key); + $ent->{obj} =3D $obj; + $ent->{rulename} =3D $rulename; +@@ -650,4 +655,5 @@ + my $ent =3D { + key =3D> $key, ++ timeout =3D> $scanner->{conf}->{rbl_timeout}, + type =3D> "URI-".$type, + id =3D> $id, +@@ -675,7 +681,4 @@ + elsif ($type eq 'URI-DNSBL') { + $self->complete_dnsbl_lookup ($scanner, $ent, $val); +- my $totalsecs =3D (time - $ent->{obj}->{querystart}); +- dbg("uridnsbl: query for ".$ent->{obj}->{dom}." took ". +- $totalsecs." seconds to look up ($val)"); + } + } +@@ -684,10 +687,9 @@ +=20 + sub res_bgsend { +- my ($self, $scanner, $host, $type) =3D @_; ++ my ($self, $scanner, $host, $type, $key) =3D @_; +=20 + return $self->{main}->{resolver}->bgsend($host, $type, undef, sub { +- my $pkt =3D shift; +- my $id =3D shift; +- $scanner->{async}->set_response_packet($id, $pkt); ++ my ($pkt, $id, $timestamp) =3D @_; ++ $scanner->{async}->set_response_packet($id, $pkt, $key, = $timestamp); + }); + } +--- Mail/SpamAssassin/Util/DependencyInfo.pm Mon Jul 23 16:46:37 2007 ++++ lib/Mail/SpamAssassin/Util/DependencyInfo.pm Fri Aug 10 11:44:15 = 2007 +@@ -140,6 +140,7 @@ + module =3D> 'Time::HiRes', + version =3D> '0.00', +- desc =3D> 'If this module is installed, the processing times are = logged/reported +- more precisely in spamd.', ++ desc =3D> 'If this module is installed, asynchronous DNS lookup = timeouts operate ++ with subsecond precision and the processing times are = logged/reported ++ more accurately. Other modules and plugins may benefit too.', + }, + { + diff -bBru /var/tmp/p5-Mail-SpamAssassin/files/patch-sa-compile.raw = ./files/patch-sa-compile.raw --- /var/tmp/p5-Mail-SpamAssassin/files/patch-sa-compile.raw Sat Jun 16 = 18:17:03 2007 +++ ./files/patch-sa-compile.raw Wed Jul 25 20:23:48 2007 @@ -1,6 +1,6 @@ ---- sa-compile.raw.orig Tue May 1 09:54:33 2007 -+++ sa-compile.raw Thu May 3 13:35:23 2007 -@@ -643,7 +643,7 @@ +--- sa-compile.raw.orig Mon Jul 23 10:47:06 2007 ++++ sa-compile.raw Wed Jul 25 20:23:14 2007 +@@ -659,9 +659,9 @@ -p prefs, --prefspath=3Dfile, --prefs-file=3Dfile Set user preferences file --siteconfigpath=3Dpath Path for site configs @@ -10,7 +10,9 @@ - (default: = /var/lib/spamassassin/compiled/) + (default: @@LOCAL_STATE_DIR@@/compiled/) --cf=3D'config line' Additional line of configuration -@@ -692,12 +692,12 @@ + -D, --debug [area=3Dn,...] Print debugging messages + -V, --version Print version +@@ -708,12 +708,12 @@ =3Ditem B<-C> I, B<--configpath>=3DI, = B<--config-file>=3DI =20 Use the specified path for locating the distributed configuration = files. diff -bBru /var/tmp/p5-Mail-SpamAssassin/pkg-install ./pkg-install --- /var/tmp/p5-Mail-SpamAssassin/pkg-install Sun Jul 22 18:40:58 2007 +++ ./pkg-install Mon Jul 23 08:11:12 2007 @@ -1,5 +1,5 @@ #!/bin/sh -PKG_PREFIX=3D${PKG_PREFIX:=3D/usr/local} +PKG_PREFIX=3D${PKG_PREFIX:-/usr/local} =20 ask() { local question default answer ------=_NextPart_000_0002_01C7DB55.AB82D610--