Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:p_conrad:branches
amavisd-new
amavisd-qmqpqq-local.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File amavisd-qmqpqq-local.patch of Package amavisd-new
--- amavisd-new-2.11.1/amavisd.orig 2019-08-04 21:46:50.260220851 +0200 +++ amavisd-new-2.11.1/amavisd 2019-08-04 21:45:47.024815454 +0200 @@ -108,6 +108,7 @@ # Amavis::In::AMPDP # Amavis::In::SMTP #( Amavis::In::Courier ) +# Amavis::In::QMQPqq # Amavis::Out::SMTP::Protocol # Amavis::Out::SMTP::Session # Amavis::Out::SMTP @@ -12113,6 +12114,7 @@ $extra_code_sql_base $extra_code_sql_log $extra_code_sql_quar $extra_code_sql_lookup $extra_code_ldap $extra_code_in_ampdp $extra_code_in_smtp $extra_code_in_courier + $extra_code_in_qmqpqq $extra_code_out_smtp $extra_code_out_pipe $extra_code_out_bsmtp $extra_code_out_local $extra_code_p0f $extra_code_antivirus $extra_code_antispam @@ -12140,6 +12142,7 @@ # Amavis::In::AMPDP, Amavis::In::SMTP and In::Courier objects use vars qw($ampdp_in_obj $smtp_in_obj $courier_in_obj); +use vars qw($qmqpqq_in_obj); # Amavis::In::QMQPqq object use vars qw($sql_dataset_conn_lookups); # Amavis::Out::SQL::Connection object use vars qw($sql_dataset_conn_storage); # Amavis::Out::SQL::Connection object @@ -12958,6 +12961,7 @@ do_log(1,"AM.PDP-in proto code%s loaded", $extra_code_in_ampdp ?'':" NOT"); do_log(1,"SMTP-in proto code %s loaded", $extra_code_in_smtp ?'':" NOT"); do_log(1,"Courier proto code %s loaded", $extra_code_in_courier ?'':" NOT"); + do_log(1,"QMQPqq-in proto code%s loaded", $extra_code_in_qmqpqq ?'':" NOT"); do_log(1,"SMTP-out proto code %s loaded", $extra_code_out_smtp ?'':" NOT"); do_log(1,"Pipe-out proto code %s loaded", $extra_code_out_pipe ?'':" NOT"); do_log(1,"BSMTP-out proto code%s loaded", $extra_code_out_bsmtp ?'':" NOT"); @@ -13691,7 +13695,11 @@ } elsif ($suggested_protocol eq 'COURIER') { die "unavailable support for protocol: $suggested_protocol"; } elsif ($suggested_protocol eq 'QMQPqq') { - die "unavailable support for protocol: $suggested_protocol"; + if (!$extra_code_in_qmqpqq) { + die "incoming TCP connection, but dynamic QMQPqq code not loaded"; + } + $qmqpqq_in_obj = Amavis::In::QMQPqq->new if !$qmqpqq_in_obj; + $qmqpqq_in_obj->process_qmqpqq_request($sock,$conn,\&check_mail); } elsif ($suggested_protocol eq 'TCP-LOOKUP') { #postfix maps, experimental process_tcp_lookup_request($sock, $conn); do_log(2, "%s", Amavis::Timing::report()); # report elapsed times @@ -13721,6 +13729,7 @@ do_log(-1, "Requesting process rundown after fatal error"); } undef $smtp_in_obj; undef $ampdp_in_obj; undef $courier_in_obj; + undef $qmqpqq_in_obj; $self->done(1); } elsif (defined $max_requests && $max_requests > 0 && $child_task_count >= $max_requests) { @@ -13729,10 +13738,12 @@ do_log(2, "Requesting process rundown after %d tasks (and %s sessions)", $child_task_count, $child_invocation_count); undef $smtp_in_obj; undef $ampdp_in_obj; undef $courier_in_obj; + undef $qmqpqq_in_obj; $self->done(1); } elsif ($extra_code_antivirus && Amavis::AV::sophos_savi_stale() ) { do_log(0, "Requesting process rundown due to stale Sophos virus data"); undef $smtp_in_obj; undef $ampdp_in_obj; undef $courier_in_obj; + undef $qmqpqq_in_obj; $self->done(1); } my(@modules_extra) = grep(!exists $modules_basic{$_}, keys %INC); @@ -18906,6 +18917,7 @@ $extra_code_zmq, $extra_code_db, $extra_code_sql_lookup, $extra_code_ldap, $extra_code_in_ampdp, $extra_code_in_smtp, $extra_code_in_courier, + $extra_code_in_qmqpqq, $extra_code_out_smtp, $extra_code_out_pipe, $extra_code_out_bsmtp, $extra_code_out_local, $extra_code_p0f, $extra_code_redis, @@ -19257,7 +19269,13 @@ } else { undef $extra_code_in_courier; } - if ($needed_protocols_in{'QMQPqq'}) { die "In::QMQPqq code not available" } + if ($needed_protocols_in{'QMQPqq'}) { + eval $extra_code_in_qmqpqq or die "Problem in the In::QMQPqq code: $@"; + # release memory occupied by the source code + undef $extra_code_in_qmqpqq; $extra_code_in_qmqpqq = 1; + } else { + undef $extra_code_in_qmqpqq; + } } if (!@lookup_sql_dsn) { undef $extra_code_sql_lookup } @@ -23506,6 +23524,279 @@ 1; +__DATA__ +# +package Amavis::In::QMQPqq; +use strict; +# use re 'taint'; # (is this module ready for this yet?) + +BEGIN { + use Exporter (); + use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); + $VERSION = '1.18'; + @ISA = qw(Exporter); +} +use POSIX qw(strftime); +use Errno qw(ENOENT); + +BEGIN { + import Amavis::Conf qw(:platform :confvars :dynamic_confvars c cr ca); + import Amavis::Util qw(ll do_log am_id new_am_id prolong_timer + debug_oneshot sanitize_str rmdir_recursively); + import Amavis::Lookup qw(lookup); + import Amavis::Timing qw(section_time); + import Amavis::rfc2821_2822_Tools; + import Amavis::TempDir; + import Amavis::In::Message; + import Amavis::In::Connection; +} + +sub new($) { + my($class) = @_; + my($self) = bless {}, $class; + $self->{bytesleft} = undef; # bytes left for whole package + $self->{len} = undef; # set by getlen() method + $self->{sock} = undef; # connected socket + $self->{proto} = undef; # protocol + $self->{tempdir} = Amavis::TempDir->new; # TempDir object + $self->{session_closed_normally} = undef; # closed properly? (waited for K/Z/D) + $self; +} + +sub DESTROY { + my($self) = shift; + eval { do_log(5,"Amavis::In::QMQPqq DESTROY called, sock=%s, normal=%s", + $self->{sock}, $self->{session_closed_normally}) }; + eval { + if (ref($self->{sock}) && ! $self->{session_closed_normally}) { + $self->qmqpqq_resp("Z","Service shutting down, closing channel"); + } + }; + if ($@ ne '') + { my($eval_stat) = $@; eval { do_log(1,"QMQPqq shutdown: %s",$eval_stat) } } +} + +# get byte, die if no bytes left +sub getbyte($) { +my($self) = shift; +if(!$self->{bytesleft}--) { + die("No bytes left"); + } +if(defined($_ = $self->{sock}->getc)) { + return($_); + } +die("EOF on socket"); +} + +sub getlen($) { +my($self) = shift; +my($ch,$len); + +for(;;) { + $ch = $self->getbyte; + if($ch eq ':') { + return($self->{len} = $len); + } + if($ch !~ /^\d$/) { + die("Char '$ch' is not a number while determining length"); + } + $len .= $ch; + } +} + +sub getcomma($) { +my($self) = shift; +if($self->getbyte ne ',') { + die("Comma expected, found '$_'"); + } +} + +sub getnetstring($$) { +my($self) = shift; +($self->{sock}->read($_[0],$self->getlen) == $self->{len}) || + die("EOF on socket"); +$self->{bytesleft} -= $self->{len}; +$self->getcomma; +} + +# Accept a QMQPqq connect +# and call content checking for the message received +# +sub process_qmqpqq_request($$$$) { +my($self,$sock,$conn,$check_mail) = @_; +# $sock: connected socket from Net::Server +# $conn: information about client connection +# $check_mail: subroutine ref to be called with file handle + +$self->{proto} = "QMQPqq"; +$self->{session_closed_normally} = 0; # closed properly? +$self->{sock} = $sock; # store $sock info for getbyte() method +$self->{bytesleft} = 20; # initial bytesleft value, there should + # NEVER EVER be longer email than 10^20 (approximately) + # bytes but increase if needed ;) +$self->{len} = undef; + +my($msginfo); +my($sender,@recips); +my($len); + +new_am_id(undef, $Amavis::child_invocation_count, undef); +Amavis::Timing::init(); + +$conn->appl_proto("QMQPqq"); +my($eval_stat); +eval { + # get length of whole package + $self->{bytesleft} = $self->getlen; + + # get length of 'email' + $len = $self->getlen; + section_time('initial length determination'); + + # prepare tempdir + Amavis::check_mail_begin_task(); + $self->{tempdir}->prepare_dir; + $self->{tempdir}->prepare_file; + + $msginfo = Amavis::In::Message->new; + $msginfo->rx_time(time); + $msginfo->log_id(am_id()); + $msginfo->conn_obj($conn); + + # get 'email' + $self->{tempdir}->empty(0); + my $size = 16384; + while(($len > 0) && ($sock->read($_,($len >= $size ? $size : $size = $len)) == $size)) { + (print {$self->{tempdir}->fh} $_) || + die("Can't write to mail file: $!"); + $len -= $size; + } + if($len > 0) { + die("EOF on socket"); + } + $self->{tempdir}->fh->flush || die("Can't flush mail file: $!"); + $self->{tempdir}->fh->seek(0,1) || die("Can't seek on file: $!"); + $self->{bytesleft} -= $self->{len}; + section_time('email receiving'); + # comma has to follow + $self->getcomma; + + # get sender (presumably in unquoted form, really???) + $self->getnetstring($sender); + section_time('sender receiving'); + + # get recips (presumably in unquoted form, really???) + my $i = 0; + while($self->{bytesleft}) { + $self->getnetstring($recips[$i++]); + } + section_time('recips receiving'); + + # final comma has to follow + $self->{bytesleft} = 1; + $self->getcomma; + + $msginfo->sender($sender); + $msginfo->sender_smtp(qquote_rfc2821_local($sender)); + $msginfo->recips(\@recips); + + do_log(1, sprintf("%s:%s:%s %s: %s -> %s Received: %s", + $self->{proto},$conn->socket_ip eq $inet_socket_bind ? + '' : '['.$conn->socket_ip.']', + $conn->socket_port, $self->{tempdir_pers}, + $msginfo->sender_smtp, + join(',', map { $_->recip_addr_smtp } + @{$msginfo->per_recip_data}), + join(' ', + ($msginfo->msg_size eq '' ? () + : 'SIZE='.$msginfo->msg_size), + ($msginfo->body_type eq '' ? () + : 'BODY='.$msginfo->body_type), + make_received_header_field($msginfo,0) ) + )); + + $msginfo->mail_tempdir($self->{tempdir}->path); + $msginfo->mail_text_fn($self->{tempdir}->path . '/email.txt'); + $msginfo->mail_text($self->{tempdir}->fh); + + my($smtp_resp,$exit_code,$preserve_evidence) = + &$check_mail($msginfo,0); + + if ($preserve_evidence) { $self->{tempdir}->preserve(1) } + + if ($smtp_resp !~ /^4/ && + grep { !$_->recip_done } @{$msginfo->per_recip_data}) { + die("TROUBLE/MISCONFIG: not all recipients done, ". + "\$forward_method is \"$forward_method\""); + } + + if ($smtp_resp !~ /^4/ && + grep { !$_->recip_done } @{$msginfo->per_recip_data}) { + if ($msginfo->delivery_method eq '') { + do_log(2,"not all recipients done, forward_method is empty"); + } + else { + die "TROUBLE: (MISCONFIG) not all recipients done, " . + "forward_method is: " . $msginfo->delivery_method; + } + } + + # all ok + if($smtp_resp =~ /^2/) { + $self->qmqpqq_resp("K",$smtp_resp); + } + # permanent reject + elsif($smtp_resp =~ /^5/) { + $self->qmqpqq_resp("D",$smtp_resp); + } + # temporary reject (or other error if !~ /^4/) + else { + $self->qmqpqq_resp("Z",$smtp_resp); + } + 1; +} or do { + $eval_stat = $@ ne '' ? $@ : "errno=$!"; +}; + +$self->{tempdir}->clean; +alarm(0); do_log(4,"timer stopped after QMQPqq eval"); + +if($eval_stat ne '') { + chomp $eval_stat; + do_log(0,"QMQPqq: NOTICE: $eval_stat"); + $self->qmqpqq_resp("Z","Service shutting down, $eval_stat"); + } +# report elapsed times by section for each transaction +do_log(2, "%s", Amavis::Timing::report()); + +$self->{session_closed_normally} = 1; +# closes connection after child_finish_hook +} + +# sends a QMQPqq response consisting of K/D/Z code and an optional message; +# slow down evil clients by delaying response on permanent errors +sub qmqpqq_resp($$$;$$) { +my($self,$code,$resp,$penalize,$line) = @_; +if($code !~ /^(K|Z|D)$/) { + die("Internal error(2): bad QMQPqq response code: '$code'"); + } +if($penalize) { + do_log(0,"QMQPqq: $resp; PENALIZE: $line"); + sleep 5; + section_time('QMQPqq penalty wait'); + } +$resp = sanitize_str($resp,1); +do_log(4,"QMQPqq> $resp"); +$self->{sock}->print($self->netstring($code . $resp)); +} + +sub netstring($$) { +my($self,$string) = @_; +return(sprintf("%d:%s,",length($string),$string)); +} + +1; + __DATA__ # package Amavis::Out::SMTP::Protocol;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor