diff -ruN policyd-weight-0.1.14.5.orig/policyd-weight policyd-weight-0.1.14.5/policyd-weight --- policyd-weight-0.1.14.5.orig/policyd-weight Thu May 10 11:57:30 2007 +++ policyd-weight-0.1.14.5/policyd-weight Wed Nov 14 18:53:05 2007 @@ -73,6 +73,7 @@ use IO::Select; use Config; use POSIX; +use MIME::Base64; use vars qw($csock $s $tcp_socket $sock $new_sock $old_mtime); @@ -308,6 +309,13 @@ my $DEBUG = 0; # 1 or 0 - don't comment +# Possible keywords to use here: +# %%MSG%% - message string contents (understood by Postfix) +# %%MSG_QUOT%% - message string content, quoted for Exim's ${extract} +# %%RATE%% - rate value +# %%RATE_INT%% - integer rate value (multiplied by 10) +my $OUTPUT_FORMAT = 'action=%%MSG%%'; + my $REJECTMSG = "550 Mail appeared to be SPAM or forged. Ask your Mail/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs"; my $REJECTLEVEL = 1; # Mails with scores which exceed this @@ -797,6 +805,7 @@ our $accepted = "UNDEF"; our $blocked = "UNDEF"; +our $instance_rate = "UNDEF"; our $my_REJECTMSG = $REJECTMSG; our %bl_err; our $skip_rel; @@ -1388,8 +1397,8 @@ my $response; - my $action; - $action = $DEFAULT_RESPONSE; + my $action = $DEFAULT_RESPONSE; + my $rate = 'UNDEF'; no strict 'refs'; @@ -1398,11 +1407,12 @@ if ($response) { - $action = $response; + $action = $response->[1]; + $rate = $response->[0]; } else { - mylog(warning=>'weighted_check returned a zero value!'); + mylog(warning=>'weighted_check returned a zero value! This should never happen!'); } # return only a restriction class if the user requested it with @@ -1412,8 +1422,9 @@ $action =~ s/^[ \t]*rc:[ \t]*(.*?)[,; .]+.*/$1/i; } - mylog(info=>"decided action=$action; delay: ".(time - $delay_time)."s"); - return("action=$action\n\n"); + mylog(info=>"decided rate=$rate; action=$action; delay: ".(time - $delay_time)."s"); + + return (weighted_check_output($rate, $action) . "\n\n"); } @@ -1440,6 +1451,38 @@ #------------------------------------------------------------------------------ # Plugin: weighted_check #------------------------------------------------------------------------------ + +sub weighted_check_output +{ + my $rate = shift or return undef; + my $message = shift or return undef; + + my $message_quoted = $message; + $message_quoted =~ s/(['"\\])/\\$1/g; + + my ($rate_int_s, $rate_s, $out); + + if ( $rate eq 'UNDEF' ) + { + $rate_s = 'UNDEF'; + $rate_int_s = 'UNDEF'; + } + else + { + $rate_s = myrnd($rate); + $rate_int_s = sprintf ('%d', int(10*$rate + .5 * ($rate <=> 0))); + } + + $out = $OUTPUT_FORMAT; + + $out =~ s/\%\%MSG\%\%/$message/g; + $out =~ s/\%\%MSG_QUOT\%\%/$message/g; + $out =~ s/\%\%RATE\%\%/$rate_s/g; + $out =~ s/\%\%RATE_INT\%\%/$rate_int_s/g; + + return $out; +} + sub weighted_check { local %_ = @_; @@ -1447,11 +1490,11 @@ my $ip = $attr{client_address}; my $cl_hostname = $attr{client_name}; - my $cansw; + my ( $cansw, $cansw_rate, $cansw_msg ); if(index($ip,":") != -1) { - return ('DUNNO IPv6'); # we have no IPv6 support for now + return ['UNDEF', 'DUNNO IPv6']; # we have no IPv6 support for now } my $client_name = $attr{client_name} || ''; @@ -1468,22 +1511,22 @@ } if($from eq '') { - return('DUNNO NULL (<>) Sender'); + return ['UNDEF', 'DUNNO NULL (<>) Sender']; } my $orig_from = $from; if($attr{recipient} && $attr{recipient} =~ /^(postmaster|abuse)\@/) { - return('DUNNO mail for '.$attr{recipient}); + return ['UNDEF', 'DUNNO mail for '.$attr{recipient}]; } if(($instance) && ($instance eq $accepted)) { - return ('DUNNO multirecipient-mail - already accepted by previous query'); + return [$instance_rate, 'DUNNO multirecipient-mail - already accepted by previous query']; } - elsif(($instance) && ($instance eq $blocked)) + elsif(($instance) && ($instance eq $blocked)) { - return ($my_REJECTMSG.' (multirecipient mail)'); + return [$instance_rate, $my_REJECTMSG.' (multirecipient mail)']; } ## cache check @@ -1492,18 +1535,30 @@ $cansw = cache_query('ask', $ip, '0', $orig_from, $from_domain); } + if ( $cansw =~ /^rate=(\S+)\s+message=(\S+)/ ) + { + $cansw_rate = $1; + $cansw_msg = decode_base64($2); + } + else + { + mylog(warning=>"Could not parse cache response: $cansw"); + $cansw_rate = 'UNDEF'; + } - if($cansw && index($cansw, 'rate') != 0) + if( $cansw_rate ne 'UNDEF' && index($cansw_msg, 'rate') != 0) { $blocked = $instance; - $my_REJECTMSG = $cansw; + $instance_rate = $cansw_rate; + $my_REJECTMSG = $cansw_msg; - return($my_REJECTMSG); + return [$cansw_rate, $my_REJECTMSG]; } - elsif($cansw && index($cansw, 'rate:hard:') == 0) + elsif($cansw ne 'UNDEF' && index($cansw_msg, 'rate:hard:') == 0) { $accepted = $instance; - return("$RETANSW $POSCACHEMSG; $cansw"); + $instance_rate = $cansw_rate; + return [$cansw_rate, "$RETANSW $POSCACHEMSG; $cansw_msg"]; } ## startup checks and preparing ############################################### @@ -1571,7 +1626,8 @@ if($maxdnserr-- <= 1) { $accepted = $instance; - return "$RETANSW $MAXDNSERRMSG in ".$dnsbl_score[$i].' lookups'; + $instance_rate = $rate; + return [$rate, "$RETANSW $MAXDNSERRMSG in ".$dnsbl_score[$i].' lookups' ]; } $RET .= ' '.$dnsbl_score[$i+3].'=ERR('.$dnsbl_score[$i+2].')'; $rate += $dnsbl_score[$i+2]; @@ -1637,23 +1693,25 @@ cache_query('nadd', $ip, $total_dnsbl_score); } $blocked = $instance; + $instance_rate = $rate; mylog(info=>"weighted check: $RET, rate: $rate"); - return($MAXDNSBLMSG.'; check http://rbls.org/?q='.$ip); + return [$rate, $MAXDNSBLMSG.'; check http://rbls.org/?q='.$ip]; } } } if($dnsbl_checks_only == 1) { - return("DUNNO only DNSBL check requested"); + return [$rate, "DUNNO only DNSBL check requested"]; } ## postive cache check - if($cansw && ($POSCACHESIZE > 0) && ($dnsbl_hits < 1)) + if($cansw_rate ne 'UNDEF' && ($POSCACHESIZE > 0) && ($dnsbl_hits < 1)) { $accepted = $instance; - return("$RETANSW $POSCACHEMSG; $cansw"); + $instance_rate = $cansw_rate; + return [$cansw_rate, "$RETANSW $POSCACHEMSG; $cansw_msg"]; } @@ -1702,7 +1760,8 @@ if($maxdnserr-- <= 1) { $accepted = $instance; - return("$RETANSW $MAXDNSERRMSG in $MATCH_TYPE MX lookups for $testhelo"); + $instance_rate = $rate; + return [$rate, "$RETANSW $MAXDNSERRMSG in $MATCH_TYPE MX lookups for $testhelo"]; } next; @@ -1728,7 +1787,8 @@ if($maxdnserr-- <= 1) { $accepted = $instance; - return("$RETANSW $MAXDNSERRMSG in $MATCH_TYPE MX -> A lookups"); + $instance_rate = $rate; + return [$rate, "$RETANSW $MAXDNSERRMSG in $MATCH_TYPE MX -> A lookups"]; } next; } @@ -1791,7 +1851,8 @@ if($maxdnserr-- <= 1) { $accepted = $instance; - return("$RETANSW $MAXDNSERRMSG in $MATCH_TYPE A lookup for $testhelo"); + $instance_rate = $rate; + return [$rate, "$RETANSW $MAXDNSERRMSG in $MATCH_TYPE A lookup for $testhelo"]; } next; } @@ -2297,8 +2358,9 @@ if($maxdnserr-- <= 1) { $accepted = $instance; - return ("$RETANSW $MAXDNSERRMSG in " . - $rhsbl_score[$i].' lookups'); + $instance_rate = $rate; + return [$rate, "$RETANSW $MAXDNSERRMSG in " . + $rhsbl_score[$i].' lookups']; } next; } @@ -2339,12 +2401,13 @@ if(($dnserr == 1) && ($dnsbl_hits < 2)) # applies if not too { # much dnsbl listed my $my_DNSERRMSG = $DNSERRMSG . ' Your HELO: '.$helo.', IP: '.$ip; - return($my_DNSERRMSG); + return ['UNDEF', $my_DNSERRMSG]; } if($rate >= $REJECTLEVEL) { $blocked = $instance; + $instance_rate = $rate; $my_REJECTMSG = $REJECTMSG; @@ -2380,15 +2443,16 @@ } } + if(($helo_ok != 1) && ($helo_untrusted_ok != 1)) { my $EREJECTMSG = $my_REJECTMSG . '; MTA helo: '.$helo.', MTA hostname: ' . $client_name.'['.$ip.'] (helo/hostname mismatch)'; - return($EREJECTMSG.$RHSBLMSG.$RELAYMSG.$DYN_DNS_MSG); + return [$rate, $EREJECTMSG.$RHSBLMSG.$RELAYMSG.$DYN_DNS_MSG]; } - return($my_REJECTMSG.$RHSBLMSG.$RELAYMSG.$DYN_DNS_MSG); + return [$rate, $my_REJECTMSG.$RHSBLMSG.$RELAYMSG.$DYN_DNS_MSG]; } else { @@ -2397,7 +2461,8 @@ cache_query('padd', $ip, $rate, $orig_from, $from_domain); } $accepted = $instance; - return("$RETANSW $RET, rate: $rate"); + $instance_rate = $rate; + return [$rate, "$RETANSW $RET, rate: $rate"]; } } @@ -2695,6 +2760,8 @@ if($query eq 'ask') { + my $rate = 'UNDEF'; + my $ret_message = 'DUNNO'; # check whether IP or IP-Sender are in SPAM cache foreach my $ckey ($ip, $ip.'-'.$sender) @@ -2710,7 +2777,8 @@ # NTTL reached and client retried it # after NTIME seconds - $ret = '0'; + $ret_message = 'DUNNO'; + $rate = 'UNDEF'; delete($cache{$ckey}); --$cache_cnt; @@ -2721,7 +2789,8 @@ { $cache{$ckey}[1] -= 1; } - $ret = $CACHEREJECTMSG. + $rate = $cache{$ckey}[0]; + $ret_message = $CACHEREJECTMSG. ' - retrying too fast. penalty: '. $NTIME.' seconds x '. $cache{$ckey}[1].' retries.'; @@ -2731,14 +2800,14 @@ } } - if(!($ret)) + if($rate eq 'UNDEF') { # ask the HAM cache my $ckey = $ip.'-'.$domain; if($poscache{$ckey}) { - $ret = "rate: "; + $ret_message = "rate: "; # check entry time if($time - $poscache{$ckey}[3] > $my_TEMP_PTIME) @@ -2748,7 +2817,7 @@ $my_PTIME) ) { - $ret = "rate:hard: "; + $ret_message = "rate:hard: "; $poscache{$ckey}[1] -= 1; } else @@ -2757,11 +2826,15 @@ $poscache{$ckey}[4] = $time; } } - $ret .= $poscache{$ckey}[0]; + $ret_message .= $poscache{$ckey}[0]; $poscache{$ckey}[2] = $time; + $rate = $poscache{$ckey}[0]; } } + + $ret = sprintf 'rate=%s message=%s', + myrnd($rate), encode_base64($ret_message, ""); } diff -ruN policyd-weight-0.1.14.5.orig/policyd-weight.conf.sample policyd-weight-0.1.14.5/policyd-weight.conf.sample --- policyd-weight-0.1.14.5.orig/policyd-weight.conf.sample Thu May 10 10:20:00 2007 +++ policyd-weight-0.1.14.5/policyd-weight.conf.sample Wed Nov 14 18:55:21 2007 @@ -5,6 +5,17 @@ $DEBUG = 0; # 1 or 0 - don't comment + $OUTPUT_FORMAT = 'action=%%MSG%%'; + # Default format is understood by Postfix. + # Possible keywords to use here: + # %%MSG%% - message string contents; understood by Postfix + # "action=" target + # %%MSG_QUOT%% - message with backslashes, quotes and + # double-quotes escaped with a backslash; suitable + # for Exim's ${extract} + # %%RATE%% - rate value + # %%RATE_INT%% - integer rate value (multiplied by 10) + $REJECTMSG = "550 Mail appeared to be SPAM or forged. Ask your Mail/DNS-Administrator to correct HELO and DNS MX settings or to get removed from DNSBLs"; $REJECTLEVEL = 1; # Mails with scores which exceed this