From 82a32ed558fb7a4167fe68d6fd079a8d7ced5745 Mon Sep 17 00:00:00 2001 From: John Peacock Date: Wed, 1 Mar 2006 16:46:55 +0000 Subject: [PATCH] Automatically disconnect DENY'd server if it doesn't go willingly. Implement queue_pre and queue_post hooks. git-svn-id: https://svn.perl.org/qpsmtpd/branches/0.3x@626 958fd67b-6ff1-0310-b445-bb7760255be9 --- lib/Qpsmtpd.pm | 8 ++++++++ lib/Qpsmtpd/Plugin.pm | 8 +++++--- lib/Qpsmtpd/SMTP.pm | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/Qpsmtpd.pm b/lib/Qpsmtpd.pm index f294ca3..fd43bbd 100644 --- a/lib/Qpsmtpd.pm +++ b/lib/Qpsmtpd.pm @@ -433,6 +433,14 @@ sub auth_mechanism { my $self = shift; return (defined $self->{_auth_mechanism} ? $self->{_auth_mechanism} : "" ); } + +sub denied { + my ($self, $value) = @_; + $self->transaction->{_denied} = $value if defined $value; + return (defined $self->transaction->{_denied} + ? $self->transaction->{_denied} + : "" ); +} 1; diff --git a/lib/Qpsmtpd/Plugin.pm b/lib/Qpsmtpd/Plugin.pm index f7250f7..3cf810b 100644 --- a/lib/Qpsmtpd/Plugin.pm +++ b/lib/Qpsmtpd/Plugin.pm @@ -2,11 +2,13 @@ package Qpsmtpd::Plugin; use Qpsmtpd::Constants; use strict; +# more or less in the order they will fire our @hooks = qw( - logging config queue data data_post quit rcpt mail ehlo helo + logging config pre-connection connect ehlo helo auth auth-plain auth-login auth-cram-md5 - connect reset_transaction unrecognized_command disconnect - deny ok pre-connection post-connection + rcpt mail data data_post queue_pre queue queue_post + quit reset_transaction disconnect post-connection + unrecognized_command deny ok ); our %hooks = map { $_ => 1 } @hooks; diff --git a/lib/Qpsmtpd/SMTP.pm b/lib/Qpsmtpd/SMTP.pm index b24eed7..c5799b2 100644 --- a/lib/Qpsmtpd/SMTP.pm +++ b/lib/Qpsmtpd/SMTP.pm @@ -50,6 +50,12 @@ sub dispatch { $self->{_counter}++; + if ( $cmd !~ /^(rset|quit)$/ and $self->denied ) { # RFC non-compliant + $self->log(LOGWARN, "non-RFC compliant MTA disconnected"); + $self->respond(521, "non-RFC compliant MTA disconnected (#5.7.0)"); + $self->disconnect; + } + if ($cmd !~ /^(\w{1,12})$/ or !exists $self->{_commands}->{$1}) { my ($rc, $msg) = $self->run_hooks("unrecognized_command", $cmd, @_); if ($rc == DENY_DISCONNECT) { @@ -150,8 +156,10 @@ sub helo { if ($rc == DONE) { # do nothing } elsif ($rc == DENY) { + $self->denied(1); $self->respond(550, $msg); } elsif ($rc == DENYSOFT) { + $self->denied(1); $self->respond(450, $msg); } elsif ($rc == DENY_DISCONNECT) { $self->respond(550, $msg); @@ -178,8 +186,10 @@ sub ehlo { if ($rc == DONE) { # do nothing } elsif ($rc == DENY) { + $self->denied(1); $self->respond(550, $msg); } elsif ($rc == DENYSOFT) { + $self->denied(1); $self->respond(450, $msg); } elsif ($rc == DENY_DISCONNECT) { $self->respond(550, $msg); @@ -290,11 +300,13 @@ sub mail { return 1; } elsif ($rc == DENY) { + $self->denied(1); $msg ||= $from->format . ', denied'; $self->log(LOGINFO, "deny mail from " . $from->format . " ($msg)"); $self->respond(550, $msg); } elsif ($rc == DENYSOFT) { + $self->denied(1); $msg ||= $from->format . ', temporarily denied'; $self->log(LOGINFO, "denysoft mail from " . $from->format . " ($msg)"); $self->respond(450, $msg); @@ -336,10 +348,12 @@ sub rcpt { return 1; } elsif ($rc == DENY) { + $self->denied(1); $msg ||= 'relaying denied'; $self->respond(550, $msg); } elsif ($rc == DENYSOFT) { + $self->denied(1); $msg ||= 'relaying denied'; return $self->respond(450, $msg); } @@ -558,7 +572,7 @@ sub data { $self->respond(452, $msg || "Message denied temporarily"); } else { - $self->queue($self->transaction); + $self->queue($self->transaction); } # DATA is always the end of a "transaction" @@ -578,7 +592,18 @@ sub getline { sub queue { my ($self, $transaction) = @_; - my ($rc, $msg) = $self->run_hooks("queue"); + # First fire any queue_pre hooks + my ($rc, $msg) = $self->run_hooks("queue_pre"); + if ($rc == DONE) { + return 1; + } + elsif ($rc != OK and $rc != DECLINED) { + return $self->log(LOGERROR, "pre plugin returned illegal value"); + return 0; + } + + # If we got this far, run the queue hooks + ($rc, $msg) = $self->run_hooks("queue"); if ($rc == DONE) { return 1; } @@ -586,16 +611,20 @@ sub queue { $self->respond(250, ($msg || 'Queued')); } elsif ($rc == DENY) { + $self->denied(1); $self->respond(552, $msg || "Message denied"); } elsif ($rc == DENYSOFT) { + $self->denied(1); $self->respond(452, $msg || "Message denied temporarily"); } else { $self->respond(451, $msg || "Queuing declined or disabled; try again later" ); } - - + + # And finally run any queue_post hooks + ($rc, $msg) = $self->run_hooks("queue_post"); + $self->log(LOGERROR, $msg) unless $rc == OK; }