diff --git a/config.sample/plugins b/config.sample/plugins index 91e8e9b..0c170ec 100644 --- a/config.sample/plugins +++ b/config.sample/plugins @@ -6,6 +6,12 @@ # plugins/http_config for details. # http_config http://localhost/~smtpd/config/ http://www.example.com/smtp.pl?config= +# The hosts_allow module must be loaded if you want the -m / --max-from-ip / +# my $MAXCONNIP = 5; # max simultaneous connections from one IP +# settings... without this it will NOT refuse more than $MAXCONNIP connections +# from one IP! +hosts_allow + quit_fortune check_earlytalker diff --git a/lib/Qpsmtpd/TcpServer.pm b/lib/Qpsmtpd/TcpServer.pm index 46022d7..86bc5bd 100644 --- a/lib/Qpsmtpd/TcpServer.pm +++ b/lib/Qpsmtpd/TcpServer.pm @@ -84,6 +84,7 @@ sub disconnect { my $self = shift; $self->log(LOGDEBUG,"click, disconnecting"); $self->SUPER::disconnect(@_); + $self->run_hooks("post-connection"); exit; } diff --git a/qpsmtpd-forkserver b/qpsmtpd-forkserver index 3a213a9..8eb2be6 100755 --- a/qpsmtpd-forkserver +++ b/qpsmtpd-forkserver @@ -192,23 +192,34 @@ while (1) { } IO::Handle::blocking($client, 1); my ($port, $iaddr) = sockaddr_in($hisaddr); - if ($MAXCONNIP) { - my $num_conn = 1; # seed with current value - - foreach my $rip (values %childstatus) { - ++$num_conn if (defined $rip && $rip eq $iaddr); + my $localsockaddr = getsockname($client); + my ($lport, $laddr) = sockaddr_in($localsockaddr); + + my ($rc, @msg) = $qpsmtpd->run_hooks("pre-connection", + remote_ip => inet_ntoa($iaddr), + remote_port => $port, + local_ip => inet_ntoa($laddr), + local_port => $lport, + max_conn_ip => $MAXCONNIP, + child_addrs => [values %childstatus], + ); + if ($rc == DENYSOFT || $rc == DENYSOFT_DISCONNECT) { + unless ($msg[0]) { + @msg = ("Sorry, try again later"); } - - if ($num_conn > $MAXCONNIP) { - my $rem_ip = inet_ntoa($iaddr); - ::log(LOGINFO,"Too many connections from $rem_ip: " - ."$num_conn > $MAXCONNIP. Denying connection."); - $client->autoflush(1); - print $client "451 Sorry, too many connections from $rem_ip, try again later\r\n"; - close $client; - next; + &respond_client($client, 451, @msg); + close $client; + next; + } + elsif ($rc == DENY || $rc == DENY_DISCONNECT) { + unless ($msg[0]) { + @msg = ("Sorry, service not available for you"); } + &respond_client($client, 550, @msg); + close $client; + next; } + my $pid = safe_fork(); if ($pid) { # parent @@ -231,8 +242,6 @@ while (1) { ::log(LOGINFO, "Connection Timed Out"); exit; }; - my $localsockaddr = getsockname($client); - my ($lport, $laddr) = sockaddr_in($localsockaddr); $ENV{TCPLOCALIP} = inet_ntoa($laddr); # my ($port, $iaddr) = sockaddr_in($hisaddr); $ENV{TCPREMOTEIP} = inet_ntoa($iaddr); @@ -256,6 +265,7 @@ while (1) { ); $qpsmtpd->run(); + $qpsmtpd->run_hooks("post-connection"); exit; # child leaves } } @@ -265,6 +275,18 @@ sub log { $qpsmtpd->log($level,$message); } +sub respond_client { + my ($client, $code, @message) = @_; + $client->autoflush(1); + while (my $msg = shift @message) { + my $line = $code . (@message?"-":" ").$msg; + ::log(LOGDEBUG, $line); + print $client "$line\r\n" + or (::log(LOGERROR, "Could not print [$line]: $!"), return 0); + } + return 1; +} + ### routine to protect process during fork sub safe_fork {