Hey,
I didn't want to mess with the known format of relayclients and
morerelayclients, so I decided to use relayclients2 for safety.
The idea is to find the first and last IPs (in binary format) in the CIDR
notated IP range. Then do 2 binary compares to see if the client IP is
greater than the first IP, and less than the last IP.
On Gentoo Net::IP is required by Net::DNS, and Net::DNS is required for
qpsmtpd anyway, so hopefully everyone should have it already.
If relayclients2 doesn't contain any data lines, it falls back to the usual
method. Means you won't be left open if there is nothing in it, as the fall
back should catch you.
This way the handling of IPv6 address is way better. Doesn't matter what case
is used, or whether you're zero padded or not. The conversion to a binary
notation negates both.
I've tested:
With and without relayclients2 existing
With an empty relayclients2, and with just some comments
Upper and lower case v6 address
With broken zero padding
The notes in relayclients2 are to describe the format necessary.
--
Mike Williams
Index: plugins/check_relay
===================================================================
--- plugins/check_relay (revision 658)
+++ plugins/check_relay (working copy)
@@ -2,25 +2,50 @@
# $ENV{RELAYCLIENT} to see if relaying is allowed.
#
+use Net::IP qw(:PROC);
+
sub hook_connect {
my ($self, $transaction) = @_;
my $connection = $self->qp->connection;
# Check if this IP is allowed to relay
- my @relay_clients = $self->qp->config("relayclients");
- my $more_relay_clients = $self->qp->config("morerelayclients", "map");
- my %relay_clients = map { $_ => 1 } @relay_clients;
my $client_ip = $self->qp->connection->remote_ip;
- while ($client_ip) {
- if (exists($ENV{RELAYCLIENT}) or
- exists($relay_clients{$client_ip}) or
- exists($more_relay_clients->{$client_ip}))
- {
- $connection->relay_client(1);
- last;
+
+ if (my @relay_clients = $self->qp->config("relayclients2")){
+ my ($range_ip, $range_prefix, $rversion, $begin, $end, $exp_client_ip, $bin_client_ip);
+ my $cversion = ip_get_version($client_ip);
+ for (@relay_clients) {
+ ($range_ip, $range_prefix) = ip_splitprefix($_);
+ $rversion = ip_get_version($range_ip);
+ ($begin, $end) = ip_normalize($_, $rversion);
+
+ $exp_client_ip = ip_expand_address($client_ip, $cversion);
+ $bin_client_ip = ip_iptobin ($exp_client_ip, $cversion);
+
+ if (ip_bincomp($bin_client_ip, 'gt', ip_iptobin($begin, $rversion))
+ && ip_bincomp($bin_client_ip, 'lt', ip_iptobin($end, $rversion)))
+ {
+ $connection->relay_client(1);
+ last;
+ }
}
- $client_ip =~ s/(\d|\w|::)+(:|\.)?$//; # strip off another 8 bits
}
-
+ else {
+ my @relay_clients = $self->qp->config("relayclients");
+ my $more_relay_clients = $self->qp->config("morerelayclients", "map");
+ my %relay_clients = map { $_ => 1 } @relay_clients;
+ $client_ip =~ s/::/:/;
+
+ while ($client_ip) {
+ if (exists($ENV{RELAYCLIENT}) or
+ exists($relay_clients{$client_ip}) or
+ exists($more_relay_clients->{$client_ip}))
+ {
+ $connection->relay_client(1);
+ last;
+ }
+ $client_ip =~ s/(\d|\w)+(:|\.)?$//; # strip off another 8 bits
+ }
+ }
return (DECLINED);
}
Index: config.sample/relayclients2
===================================================================
--- config.sample/relayclients2 (revision 0)
+++ config.sample/relayclients2 (revision 0)
@@ -0,0 +1,11 @@
+# CIDR notation
+# legal 2001:618:400:fedf::/64
+# illegal 2001:618:400:fedf::/48 !
+# legal 2001:618:400::/48
+# legal 192.168.0.0/24
+# illegal 192.168.30.0/16 !
+# legal 192.168.30.0/24
+
+2001:618:400:fedf::/64
+192.168.0.0/24
+192.168.30.0/24