Server IP : 85.214.239.14 / Your IP : 3.135.247.237 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/2/cwd/usr/share/perl5/Amavis/Out/ |
Upload File : |
# SPDX-License-Identifier: GPL-2.0-or-later package Amavis::Out::BSMTP; 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); @EXPORT = qw(&mail_via_bsmtp); } use Errno qw(ENOENT EACCES); use IO::File qw(O_CREAT O_EXCL O_WRONLY); use Amavis::Conf qw(:platform $QUARANTINEDIR c cr ca); use Amavis::Out::EditHeader; use Amavis::rfc2821_2822_Tools; use Amavis::Timing qw(section_time); use Amavis::Util qw(untaint min max minmax ll do_log snmp_count idn_to_ascii collect_equal_delivery_recips); # store message in a BSMTP format # # RFC 2442: Application/batch-SMTP material is generated by a specially # modified SMTP client operating without a corresponding SMTP server. # The client simply assumes a successful response to all commands it issues. # The resulting content then consists of the collected output from the SMTP # client. # sub mail_via_bsmtp(@) { my($msginfo, $initial_submission, $dsn_per_recip_capable, $filter) = @_; my(@snmp_vars) = !$initial_submission ? ('', 'Relay', 'ProtoBSMTP', 'ProtoBSMTPRelay') : ('', 'Submit', 'ProtoBSMTP', 'ProtoBSMTPSubmit', 'Submit'.$initial_submission); snmp_count('OutMsgs'.$_) for @snmp_vars; my $logmsg = sprintf("%s via BSMTP: %s", ($initial_submission?'SEND':'FWD'), $msginfo->sender_smtp); my($per_recip_data_ref, $proto_sockname) = collect_equal_delivery_recips($msginfo, $filter, qr/^bsmtp:/i); if (!$per_recip_data_ref || !@$per_recip_data_ref) { do_log(5, "%s, nothing to do", $logmsg); return 1; } $proto_sockname = $proto_sockname->[0] if ref $proto_sockname; ll(1) && do_log(1, "delivering to %s, %s -> %s", $proto_sockname, $logmsg, join(',', qquote_rfc2821_local( map($_->recip_final_addr, @$per_recip_data_ref)) )); # just use the first one, ignoring failover alternatives local($1); $proto_sockname =~ /^bsmtp:(.*)\z/si or die "Bad fwd method syntax: ".$proto_sockname; my $bsmtp_file_final = $1; my $mbxname; my $s = $msginfo->sender; # sanitized sender name for use in a filename $s =~ tr/a-zA-Z0-9@._+-/=/c; substr($s,100) = '...' if length($s) > 100+3; $s =~ s/\@/_at_/g; $s =~ s/^(\.{0,2})\z/_$1/; $bsmtp_file_final =~ s{%(.)} { $1 eq 'b' ? $msginfo->body_digest : $1 eq 'P' ? $msginfo->partition_tag : $1 eq 'm' ? $msginfo->mail_id||'' : $1 eq 'n' ? $msginfo->log_id : $1 eq 's' ? untaint($s) # a hack, avoid using %s : $1 eq 'i' ? iso8601_timestamp($msginfo->rx_time,1) #,'-') : $1 eq '%' ? '%' : '%'.$1 }gse; # prepend directory if not specified my $bsmtp_file_final_to_show = $bsmtp_file_final; $bsmtp_file_final = $QUARANTINEDIR."/".$bsmtp_file_final if $QUARANTINEDIR ne '' && $bsmtp_file_final !~ m{^/}; my $bsmtp_file_tmp = $bsmtp_file_final . ".tmp"; my $mp; my $err; eval { my $errn = lstat($bsmtp_file_tmp) ? 0 : 0+$!; if ($errn == ENOENT) {} # good, no file, as expected elsif ($errn==0 && (-f _ || -l _)) { die "File $bsmtp_file_tmp already exists, refuse to overwrite" } else { die "File $bsmtp_file_tmp exists??? Refuse to overwrite it, $!" } $mp = IO::File->new; # O_WRONLY etc. can become tainted in Perl5.8.9 [perlbug #62502] $mp->open($bsmtp_file_tmp, untaint(O_CREAT|O_EXCL|O_WRONLY), 0640) or die "Can't create BSMTP file $bsmtp_file_tmp: $!"; binmode($mp,':bytes') or die "Can't set :bytes, $!"; # RFC 2442: Since no SMTP server is present the client must be prepared # to make certain assumptions about which SMTP extensions can be used. # The generator MAY assume that ESMTP [RFC 1869 (obsoleted by RFC 5321)] # facilities are available, that is, it is acceptable to use the EHLO # command and additional parameters on MAIL FROM and RCPT TO. If EHLO # is used MAY assume that the 8bitMIME [RFC 6152], SIZE [RFC 1870], and # NOTARY [RFC 1891] extensions are available. In particular, NOTARY # SHOULD be used. (nowadays called DSN) my $myheloname = c('localhost_name'); # host name used in EHLO/HELO/LHLO $myheloname = 'localhost' if $myheloname eq ''; $myheloname = idn_to_ascii($myheloname); $mp->printf("EHLO %s\n", $myheloname) or die "print failed (EHLO): $!"; my $btype = $msginfo->body_type; # RFC 6152: need "8bit Data"? (RFC 2045) $btype = '' if !defined $btype; my $dsn_envid = $msginfo->dsn_envid; my $dsn_ret = $msginfo->dsn_ret; $mp->printf("MAIL FROM:%s\n", join(' ', $msginfo->sender_smtp, $btype ne '' ? ('BODY='.uc($btype)) : (), defined $dsn_ret ? ('RET='.$dsn_ret) : (), defined $dsn_envid ? ('ENVID='.$dsn_envid) : () ), ) or die "print failed (MAIL FROM): $!"; for my $r (@$per_recip_data_ref) { my(@dsn_notify); # implies a default when the list is empty my $dn = $r->dsn_notify; @dsn_notify = @$dn if $dn && $msginfo->sender ne ''; # if nondefault if (@dsn_notify && c('terminate_dsn_on_notify_success')) { # we want to handle option SUCCESS locally if (grep($_ eq 'SUCCESS', @dsn_notify)) { # strip out SUCCESS @dsn_notify = grep($_ ne 'SUCCESS', @dsn_notify); @dsn_notify = ('NEVER') if !@dsn_notify; do_log(3,"stripped out SUCCESS, result: NOTIFY=%s", join(',',@dsn_notify)); } } $mp->printf("RCPT TO:%s\n", join(' ', qquote_rfc2821_local($r->recip_final_addr), @dsn_notify ? ('NOTIFY='.join(',',@dsn_notify)) : (), defined $r->dsn_orcpt ? ('ORCPT='.$r->dsn_orcpt) : () ), ) or die "print failed (RCPT TO): $!"; } $mp->print("DATA\n") or die "print failed (DATA): $!"; my $hdr_edits = $msginfo->header_edits; $hdr_edits = Amavis::Out::EditHeader->new if !$hdr_edits; my($received_cnt,$file_position) = $hdr_edits->write_header($msginfo,$mp,!$initial_submission); 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; if ($received_cnt > 100) { # loop detection required by RFC 5321 sect. 6.3 die "Too many hops: $received_cnt 'Received:' header fields"; } elsif (!defined $msg) { # empty mail } elsif (ref $msg eq 'SCALAR') { my $buff = substr($$msg,$file_position); $buff =~ s/^\./../gm; $mp->print($buff) or die "print failed - data: $!"; } elsif ($msg->isa('MIME::Entity')) { $msg->print_body($mp); } else { my $ln; for ($! = 0; defined($ln=$msg->getline); $! = 0) { $mp->print($ln=~/^\./ ? (".",$ln) : $ln) or die "print failed - data: $!"; } defined $ln || $! == 0 or die "Error reading: $!"; } $mp->print(".\n") or die "print failed (final dot): $!"; # $mp->print("QUIT\n") or die "print failed (QUIT): $!"; $mp->close or die "Error closing BSMTP file $bsmtp_file_tmp: $!"; undef $mp; rename($bsmtp_file_tmp, $bsmtp_file_final) or die "Can't rename BSMTP file to $bsmtp_file_final: $!"; $mbxname = $bsmtp_file_final; 1; } or do { $err = $@ ne '' ? $@ : "errno=$!" }; my $smtp_response; if ($err eq '') { $smtp_response = "250 2.6.0 Ok, queued as BSMTP $bsmtp_file_final_to_show"; snmp_count('OutMsgsDelivers'); my $size = $msginfo->msg_size; snmp_count( ['OutMsgsSize'.$_, $size, 'C64'] ) for @snmp_vars; } else { chomp $err; unlink($bsmtp_file_tmp) or do_log(-2,"Can't delete half-finished BSMTP file %s: %s", $bsmtp_file_tmp, $!); $mp->close if defined $mp; # ignore status if ($err =~ /too many hops\b/i) { $smtp_response = "554 5.4.6 Reject: $err"; snmp_count('OutMsgsRejects'); } else { $smtp_response = "451 4.5.0 Writing $bsmtp_file_tmp failed: $err"; snmp_count('OutMsgsAttemptFails'); } die $err if $err =~ /^timed out\b/; # resignal timeout } $smtp_response .= ", id=" . $msginfo->log_id; $msginfo->dsn_passed_on($smtp_response=~/^2/ && !c('terminate_dsn_on_notify_success') ? 1 : 0); for my $r (@$per_recip_data_ref) { next if $r->recip_done; $r->recip_smtp_response($smtp_response); $r->recip_done(2); $r->recip_mbxname($mbxname) if $mbxname ne '' && $smtp_response =~ /^2/; } section_time('fwd-bsmtp'); 1; } 1;