Server IP : 85.214.239.14 / Your IP : 18.217.241.235 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/3/cwd/proc/3/root/usr/share/perl5/Amavis/SpamControl/ |
Upload File : |
# SPDX-License-Identifier: GPL-2.0-or-later package Amavis::SpamControl::SpamdClient; use strict; use re 'taint'; use warnings; use warnings FATAL => qw(utf8 void); no warnings 'uninitialized'; # use warnings 'extra'; no warnings 'experimental::re_strict'; use re 'strict'; BEGIN { require Exporter; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); $VERSION = '2.412'; @ISA = qw(Exporter); } use Errno qw(ENOENT EACCES); use Amavis::Conf qw(:platform :confvars :sa c cr ca); use Amavis::rfc2821_2822_Tools qw(qquote_rfc2821_local); use Amavis::Timing qw(section_time); use Amavis::Util qw(ll do_log sanitize_str min max minmax get_deadline); sub new { my($class, $scanner_name,$module,@args) = @_; my(%options) = @args; bless { scanner_name => $scanner_name, options => \%options }, $class; } # needs spamd running, could be started like this: # spamd -H /var/amavis/home -r /var/amavis/home/spamd.pid -s stderr \ # -u vscan -g vscan -x -P --allow-tell --min-children=2 --max-children=2 sub check { my($self,$msginfo) = @_; my($which_section, $spam_level, $sa_tests, $size_limit, %attr); my $scanner_name = $self->{scanner_name}; my $mbsl = $self->{options}->{'mail_body_size_limit'}; $mbsl = c('sa_mail_body_size_limit') if !defined $mbsl; if (defined $mbsl) { $size_limit = min(64*1024, $msginfo->orig_header_size) + 1 + min($mbsl, $msginfo->orig_body_size); # don't bother if slightly oversized, it's faster without size checks undef $size_limit if $msginfo->msg_size < $size_limit + 5*1024; } my $hdr_edits = $msginfo->header_edits; # fake a local delivery agent by inserting Return-Path $which_section = 'prepare pseudo header section'; my $hdr_prefix = ''; $hdr_prefix .= sprintf("Return-Path: %s\n", $msginfo->sender_smtp); $hdr_prefix .= sprintf("X-Envelope-To: %s\n", join(",\n ",qquote_rfc2821_local(@{$msginfo->recips}))); my $os_fp = $msginfo->client_os_fingerprint; $hdr_prefix .= sprintf("X-Amavis-OS-Fingerprint: %s\n", sanitize_str($os_fp)) if defined($os_fp) && $os_fp ne ''; my(@av_tests); my $per_recip_data = $msginfo->per_recip_data; $per_recip_data = [] if !$per_recip_data; for my $r (@$per_recip_data) { my $spam_tests = $r->spam_tests; push(@av_tests, grep(/^AV\..+=/, split(/,/, join(',',map($$_,@$spam_tests))))) if $spam_tests; } $hdr_prefix .= sprintf("X-Amavis-AV-Status: %s\n", sanitize_str(join(',',@av_tests))) if @av_tests; $hdr_prefix .= sprintf("X-Amavis-PolicyBank: %s\n", c('policy_bank_path')); $hdr_prefix .= sprintf("X-Amavis-MessageSize: %d%s\n", $msginfo->msg_size, !defined $size_limit ? '' : ", TRUNCATED to $size_limit"); my($remaining_time, $deadline) = get_deadline('spamd check', 1, 5); my $msg = $msginfo->mail_text; my $msg_str_ref = $msginfo->mail_text_str; # have an in-memory copy? $msg = $msg_str_ref if ref $msg_str_ref; eval { $which_section = 'spamd_connect'; do_log(3,"connecting to spamd"); my $spamd_handle = Amavis::IO::RW->new( [ '127.0.0.1:783', '[::1]:783' ], Eol => "\015\012", Timeout => 10); defined $spamd_handle or die "Can't connect to spamd, $@ ($!)"; $spamd_handle->timeout(max(3, $deadline - Time::HiRes::time)); section_time($which_section); $which_section = 'spamd_tx'; do_log(4,"sending to spamd"); $hdr_prefix =~ s{\n}{\015\012}gs; my $file_position = $msginfo->skip_bytes; my $msgsize = length($hdr_prefix); # prepended lines... $msgsize += $msginfo->msg_size; # size as defined by RFC 1870 $msgsize -= $file_position; # TODO: adjust for CRLF (alright for 0) ll(5) && do_log(5, "spamc: message size: %d + %d - %d = %s", length($hdr_prefix), $msginfo->msg_size, $file_position, defined $size_limit && $msgsize > $size_limit ? "LIM:$size_limit" : $msgsize); if (defined $size_limit && $msgsize > $size_limit) { # consider $size_limit in the RFC 1870 sense for simplicity $msgsize = $size_limit; } $spamd_handle->print("SYMBOLS SPAMC/1.3\015\012"); # HEADERS $spamd_handle->print("Content-length: " . $msgsize . "\015\012"); $spamd_handle->print("\015\012"); $spamd_handle->print($hdr_prefix); my $bytes_written = length($hdr_prefix); if (!defined $msg) { # empty mail } elsif (ref $msg eq 'SCALAR') { # do it in chunks, saves memory, cache friendly my $done; while ($file_position < length($$msg)) { my $buff = substr($$msg,$file_position,16384); $file_position += length($buff); $buff =~ s{\n}{\015\012}gs; if (defined $size_limit && $bytes_written + length($buff) >= $size_limit) { substr($buff, $size_limit - $bytes_written) = ''; # truncate # spamd reads line-by-line and hangs if not terminated by a NL substr($buff,-1,1) = "\012"; do_log(5,"spamc: reached size limit %d bytes, ". "%d = %d (sent) + %d (still to go)", $size_limit, $bytes_written+length($buff), $bytes_written, length($buff)); $done = 1; } $spamd_handle->print($buff); $bytes_written += length($buff); last if $done; } } elsif ($msg->isa('MIME::Entity')) { # TODO - content length won't match! do_log(3,"spamc: message is MIME::Entity, size won't match"); $msg->print_body($spamd_handle); } else { $msg->seek($file_position,0) or die "Can't rewind mail file: $!"; my($nbytes,$buff,$done); while ( $nbytes=$msg->sysread($buff,16384) ) { $file_position += $nbytes; $buff =~ s{\n}{\015\012}gs; if (defined $size_limit && $bytes_written + length($buff) >= $size_limit) { substr($buff, $size_limit - $bytes_written) = ''; # truncate # spamd reads line-by-line and hangs if not terminated by a NL substr($buff,-1,1) = "\012"; do_log(5,"spamc: reached size limit %d bytes, ". "%d = %d (sent) + %d (still to go)", $size_limit, $bytes_written+length($buff), $bytes_written, length($buff)); $done = 1; } $spamd_handle->print($buff); $bytes_written += length($buff); last if $done; } defined $nbytes or die "Error reading: $!"; } $spamd_handle->flush; $hdr_prefix = undef; section_time($which_section); $which_section = 'spamd_rx'; do_log(4,"receiving from spamd"); my($version, $resp_code, $resp_msg); local($1,$2,$3); my($ln,$error,$first); $first = 1; while (defined($ln = $spamd_handle->get_response_line)) { do_log(4,"from spamd - resp.hdr: %s", $ln); if ($ln eq "\015\012") { last; } elsif ($first) { $first = 0; $ln =~ s/\015\012\z//; ($version,$resp_code,$resp_msg) = split(/[ \t]+/,$ln,3); } elsif ($ln =~ /^([^:]*?)[ \t]*:[ \t]*(.*)\015\012\z/i) { $attr{lc($1)} = $2; } else { $error = $ln } } if ($first) { do_log(-1,"Empty spamd response") } elsif (defined $error) { do_log(-1,"Error in spamd resp: %s",$error) } elsif ($resp_code !~ /^\d+\z/ || $resp_code != 0) { do_log(-1,"Failure reported by spamd: %s %s %s", $version,$resp_code,$resp_msg); } else { my $reply_len = 0; while (defined($ln = $spamd_handle->get_response_line)) { do_log(5,"from spamd: %s", $ln); $reply_len += length($ln); $ln =~ s/\015\012\z//; $sa_tests = $ln; } do_log(-1,"Reply from spamd size mismatch: %d %s", $reply_len, $attr{'content-length'} ) if $reply_len != $attr{'content-length'}; } $spamd_handle->close; # terminate the session, ignoring status undef $spamd_handle; $spam_level = $2 if $attr{'spam'} =~ m{(\S+) ; (\S+) / (\S+)}; 1; } or do { my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; do_log(-1,"%s client failed: %s", $scanner_name, $eval_stat); }; section_time($which_section); my $score_factor = $self->{options}->{'score_factor'}; if (defined $spam_level && defined $score_factor) { $spam_level *= $score_factor; } do_log(2,"%s spamd score=%s, tests=%s", $scanner_name, $spam_level, $sa_tests); $msginfo->supplementary_info('SCORE-'.$scanner_name, $spam_level); $msginfo->supplementary_info('VERDICT-'.$scanner_name, $attr{'spam'} =~ /^True/ ? 'Spam' : $attr{'spam'} =~ /^False/ ? 'Ham' : 'Unknown'); for my $r (@$per_recip_data) { $r->spam_level( ($r->spam_level || 0) + $spam_level ); if (!$r->spam_tests) { $r->spam_tests([ \$sa_tests ]); } else { push(@{$r->spam_tests}, \$sa_tests); } } } 1;