#!perl -Tw =head1 NAME hosts_allow - decide if a host is allowed to send mail =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 file I. The plugin takes no arguments. =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). Example: 192.168.3.4 DECLINED 192.168.3.0/24 DENY Sorry, known spam only source This would exclude 192.168.3.4 from the DENY of 192.168.3.0/24. =cut use Qpsmtpd::Constants; use Socket; sub hook_pre_connection { my ($self,$transaction,%args) = @_; # remote_ip => inet_ntoa($iaddr), # remote_port => $port, # local_ip => inet_ntoa($laddr), # local_port => $lport, # max_conn_ip => $MAXCONNIP, # child_addrs => [values %childstatus], my $remote = $args{remote_ip}; if ($args{max_conn_ip}) { 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"); } } 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 = 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; return($const, $message); } } return (DECLINED); }