From 059771d31d4953050af5987c9da2d57d1fb764ee Mon Sep 17 00:00:00 2001 From: "Karl Y. Pradene" Date: Mon, 9 Feb 2009 22:25:51 +0100 Subject: [PATCH] End of headers hook: data_headers_end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hook after receiving all headers lines. Defaults to nothing, just continue processing. At this step, sender does not wait for a reply, but we can stop him from sending remaining data by disconnecting. (Cleaned up by Robert for english and coding style.) Signed-off-by: Ask Bjørn Hansen Signed-off-by: Robert Spier --- README.plugins | 19 +++++++++++++++++++ lib/Qpsmtpd/Plugin.pm | 2 +- lib/Qpsmtpd/SMTP.pm | 20 ++++++++++++++------ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/README.plugins b/README.plugins index 1fe37a0..13e2505 100644 --- a/README.plugins +++ b/README.plugins @@ -150,6 +150,24 @@ Hook for the "data" command. Defaults to '354, "go ahead"'. recommended) +=head2 data_headers_end + +Hook fires after all header lines of the message data has been received. +Defaults to doing nothing, just continue processing. At this step, +the sender is not waiting for a reply, but we can try and prevent him from +sending the entire message by disconnecting immediately. (Although it is +likely the packets are already in flight due to buffering and pipelining). + +BE CAREFUL! If you drop the connection legal MTAs will retry again and again, +spammers will probably not. This is not RFC compliant and can lead to +an unpredictable mess. Use with caution. + +Allowed return codes: + + DENY_DISCONNECT - Return '554 Message denied' and disconnect + DENYSOFT_DISCONNECT - Return '421 Message denied temporarily' and disconnect + DECLINED - Do nothing + =head2 data_post Hook after receiving all data; just before the message is queued. @@ -305,6 +323,7 @@ routine is: C< s/\W/_/g; > config hook_config queue hook_queue data hook_data + data_headers_end hook_data_headers_end data_post hook_data_post quit hook_quit rcpt hook_rcpt diff --git a/lib/Qpsmtpd/Plugin.pm b/lib/Qpsmtpd/Plugin.pm index f350e8b..7758788 100644 --- a/lib/Qpsmtpd/Plugin.pm +++ b/lib/Qpsmtpd/Plugin.pm @@ -7,7 +7,7 @@ our @hooks = qw( logging config post-fork pre-connection connect ehlo_parse ehlo helo_parse helo auth_parse auth auth-plain auth-login auth-cram-md5 rcpt_parse rcpt_pre rcpt mail_parse mail mail_pre - data data_post queue_pre queue queue_post vrfy noop + data data_headers_end data_post queue_pre queue queue_post vrfy noop quit reset_transaction disconnect post-connection unrecognized_command deny ok received_line help ); diff --git a/lib/Qpsmtpd/SMTP.pm b/lib/Qpsmtpd/SMTP.pm index e9492d4..2f17525 100644 --- a/lib/Qpsmtpd/SMTP.pm +++ b/lib/Qpsmtpd/SMTP.pm @@ -664,11 +664,21 @@ sub data_respond { $buffer = ""; - # FIXME - call plugins to work on just the header here; can - # save us buffering the mail content. + $self->transaction->header($header); - # Save the start of just the body itself - $self->transaction->set_body_start(); + my ($rc, $msg) = $self->run_hooks('data_headers_end'); + if ($rc == DENY_DISCONNECT) { + $self->respond(554, $msg || "Message denied"); + $self->disconnect; + return 1; + } elsif ($rc == DENYSOFT_DISCONNECT) { + $self->respond(421, $msg || "Message denied temporarily"); + $self->disconnect; + return 1; + } + + # Save the start of just the body itself + $self->transaction->set_body_start(); } @@ -687,8 +697,6 @@ sub data_respond { $self->log(LOGDEBUG, "max_size: $max_size / size: $size"); - $self->transaction->header($header); - my $smtp = $self->connection->hello eq "ehlo" ? "ESMTP" : "SMTP"; my $esmtp = substr($smtp,0,1) eq "E"; my $authheader = '';