diff --git a/config.sample/plugins b/config.sample/plugins index 451d749..71b9f5e 100644 --- a/config.sample/plugins +++ b/config.sample/plugins @@ -1,4 +1,4 @@ -# +# # Example configuration file for plugins # @@ -6,6 +6,9 @@ # plugins/http_config for details. # http_config http://localhost/~smtpd/config/ http://www.example.com/smtp.pl?config= +# hosts_allow does not work with the tcpserver deployment model! +# perldoc plugins/hosts_allow for an alternative. +# # 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 @@ -48,12 +51,12 @@ virus/klez_filter # You can run the spamassassin plugin with options. See perldoc -# plugins/spamassassin for details. +# plugins/spamassassin for details. # spamassassin # rejects mails with a SA score higher than 20 and munges the subject -# of the score is higher than 10. +# of the score is higher than 10. # # spamassassin reject_threshold 20 munge_subject_threshold 10 diff --git a/plugins/hosts_allow b/plugins/hosts_allow index 2874811..77aafd1 100644 --- a/plugins/hosts_allow +++ b/plugins/hosts_allow @@ -1,24 +1,29 @@ #!perl -w -=head1 NAME +=head1 NAME -hosts_allow - decide if a host is allowed to send mail +hosts_allow - decide if a host is allowed to connect =head1 DESCRIPTION The B module decides before the SMTP-Greeting if a host is allowed to connect. It checks for too many (running) connections from one -host (see -m/--max-from-ip options in qpsmtpd-forkserver) and the config +host (see -m/--max-from-ip options in qpsmtpd-forkserver) and the config file I. -The plugin takes no arguments. + +The plugin takes no config/plugin arguments. + +This plugin only works with the forkserver and prefork deployment models. It +does not work with the tcpserver deployment model. See SEE ALSO below. =head1 CONFIG -The config file contains lines with two or three items. The first is either -an IP address or a network/mask pair. The second is a (valid) return code -from Qpsmtpd::Constants. The last is a comment which will be returned to the -connecting client if the return code is DENY or DENYSOFT (and of course -DENY_DISCONNECT and DENYSOFT_DISCONNECT). +The I config file contains lines with two or three items. The +first is an IP address or a network/mask pair. The second is a (valid) return +code from Qpsmtpd::Constants. The last is a comment which will be returned to +the connecting client if the return code is DENY or DENYSOFT (and of course +DENY_DISCONNECT and DENYSOFT_DISCONNECT). + Example: 192.168.3.4 DECLINED @@ -26,8 +31,28 @@ Example: This would exclude 192.168.3.4 from the DENY of 192.168.3.0/24. +=head1 SEE ALSO + +To get similar functionality for the tcpserver deployment model, use +tcpserver's -x feature. Create a tcp.smtp file with entries like this: + + 70.65.227.235:deny + 183.7.90.207:deny + :allow + +compile the tcp.smtp file like this: + + /usr/local/bin/tcprules tcp.smtp.cdb tcp.smtp.tmp < tcp.smtp + +and add the file to the chain of arguments to tcpserver in your run file. + +See also: http://cr.yp.to/ucspi-tcp.html + =cut +use strict; +use warnings; + use Qpsmtpd::Constants; use Socket; @@ -39,41 +64,42 @@ sub hook_pre_connection { # local_ip => inet_ntoa($laddr), # local_port => $lport, # max_conn_ip => $MAXCONNIP, - # child_addrs => [values %childstatus], + # child_addrs => [values %childstatus], my $remote = $args{remote_ip}; + my $max = $args{max_conn_ip}; - if ($args{max_conn_ip}) { + if ( $max ) { my $num_conn = 1; # seed with current value my $raddr = inet_aton($remote); foreach my $rip (@{$args{child_addrs}}) { ++$num_conn if (defined $rip && $rip eq $raddr); } - if ($num_conn > $args{max_conn_ip}) { - $self->log(LOGINFO, - "Too many connections from $remote: " - . "$num_conn > " . $args{max_conn_ip} - . "Denying connection."); - return (DENYSOFT, "Sorry, too many connections from $remote, " - ."try again later"); + if ($num_conn > $max ) { + my $err_mess = "too many connections from $remote"; + $self->log(LOGINFO, "fail: $err_mess ($num_conn > $max)"); + return (DENYSOFT, "Sorry, $err_mess, try again later"); } } - + foreach ($self->qp->config("hosts_allow")) { s/^\s*//; my ($ipmask, $const, $message) = split /\s+/, $_, 3; next unless defined $const; my ($net,$mask) = split '/', $ipmask, 2; - if (!defined $mask) { - $mask = 32; - } + $mask = 32 if !defined $mask; $mask = pack "B32", "1"x($mask)."0"x(32-$mask); if (join(".", unpack("C4", inet_aton($remote) & $mask)) eq $net) { $const = Qpsmtpd::Constants::return_code($const) || DECLINED; + if ( $const =~ /deny/i ) { + $self->log( LOGINFO, "fail: $message" ); + }; + $self->log( LOGDEBUG, "pass: $const, $message" ); return($const, $message); } } + $self->log( LOGDEBUG, "pass" ); return (DECLINED); }