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

Reply via email to